HashMap总结

  1. 默认table数组大小为16,默认loadFactor=0.75,threshold=数组长度*loadFactor,所以默认为12。

  2. 当集合中元素当size > threshold时进行resize,数组扩充为原来当2倍。

  3. 当table数组当长度大于等于64时,冲突元素的的链表元素数量大于等于8时链表将被一颗红黑树取代。

  4. 数组index计算

index=(n - 1) & hash,n为数组长度,因为数组的长度总是2的n次方,所以n-1转换为二进制低位全部是1,这样再跟hash与运算,那么得出的index就更加取决于元素本身而不是数组的长度。

  1. 插入逻辑:

  1. 获取hashcode。

  2. 计算出hash。

  3. 计算table index,index=(n - 1) & hash,n为table长度。

  4. 判断table[index]是否为null,如果是直接将此节点放入table[index]。

  5. 否则判断table[index]是否为TreeNode如果是直接调用方法putTreeVal()将元素插入树上。

  6. 否则连接到链表的末尾。如果链表上存在元素其key和将要插入的元素的key相等的,则使用新的value替换老的value。

  7. 当table数组当长度大于等于64时,冲突元素的的链表元素数量大于等于8时链表将被一颗红黑树取代。当不大于64时进行resize操作。

  1. key的相等比较

如果有元素p满足:
p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))
则说明key已存在,和数组上元素以及链表中对元素的比较都是这种比较规则。

对于链表,如果hash相同但是key不相等则直接链接到链表上。

对于红黑树,如果hash相同但是key不相等则按照如下顺序比较出大小:

  1. 如果实现了Comparable接口则使用compareTo()方法比较大小。

  2. 比较字符串表示的类名大小同样使用compareTo()比较。

  3. 调用System.identityHashCode()方法生成hashcode比较大小,如下:
    d = (System.identityHashCode(a) <= System.identityHashCode(b) ?-1 : 1),此时一定可以比较出大小

也就是说当冲突元素当hash相等key却不同时一定要分出个大小。

  1. jdk7中HashMap并发问题。

死链问题,扩容时数据丢失,元素被覆盖。 死链问题主要发生在transfer()方法:

void transfer(Entry[] newTable) {
   Entry[] src = table; 
   int newCapacity = newTable.length;   for (int j = 0; j < src.length; j++) { 
       Entry<K, V> e = src[j];       if (e != null) {
           src[j] = null;  
           do {
               Entry<K, V> next = e.next;
               int i = indexFor(e.hash, newCapacity); 
               e.next = newTable[i]; // 1
               newTable[i] = e;      // 2
               e = next;            
           } while (e != null);
       }
   }
}

并发问题主要出现在1、2两行代码,这两行代码在并发时会互相覆盖导致死链问题。主要是因为并发修改了共享变量的next属性。
例如:数组的index=0处有个链表,分别是1和2,1的next是2,两个线程同时resize时,do while循环时,第一次正常,第二次时,第一个线程已经把两个关系设置成了2元素的next指向1,此时对于线程2:e指向2元素,next指向1元素。第二个线程,第三次循环,e指向了1元素,此时执行e.next = newTable[i],则为2元素,那么就形成了1元素的next指向2元素,2元素的next指向1元素,形成环,线程2就会一直运行下去。

  1. LinkedHashMap

LinkedHashMap遍历的时候保持了出入的顺序。其实现原理为:LinkedHashMap首先继承了HashMap,其次其内部Entry继承了HashMap.Node并多了两个属性Entry<K,V> before, after;就是这两个属性使LinkedHashMap内所有接单连成了一个双向链表,所以也就有了顺序性。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章