在学习java多线程的过程中发现JDK5中的并发操作大多是基于JDK5中新加的collection更易发挥强大的效率,就顺便看看了些java.util中的collection类。有很多文章对HashMap进行描述。详细的文章如下:
http://zhangshixi.javaeye.com/blog/672697
http://annegu.javaeye.com/blog/539465
http://yk94wo.blog.sohu.com/155806400.html
HashMap的基本特性都可以在上面文章中找到。HashMap在初始化的过程中,会保证Map的长度为2的n次方。而在添加新的元素的时候是将新元素当做当前链表第一个元素,即同一位置“新加入的放在链头,最先加入的放在链尾”(ConcurrentHashMap也是如此)。添加完成之后会判断Map元素的个数来确定是否resize(rehash):
resize函数:
void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; //容量达到极限,就无需resize if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } //resize就是将当前的Entry复制到一个新的数组中 Entry[] newTable = new Entry[newCapacity]; transfer(newTable); table = newTable; threshold = (int)(newCapacity * loadFactor); }
entry元素重定位函数transfer:
/** * Transfers all entries from current table to newTable. */ 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 {{//这里:1.一条链表上的hash值都一样,所以数组容量一致时,indexFor()返回的值一样。那么在重新放置时,只需把链表头的指针挪到新位置即可,不需要一个一个挪动。应该与clear()和put()方法异曲同工。 Entry<K,V> next = e.next;//链表中的下一个元素 int i = indexFor(e.hash, newCapacity);//元素e在新的空间中重新定位 e.next = newTable[i];//更新e.next的引用,指向当前链表的表头 newTable[i] = e;//将e作为当前链表的表头 e = next;//e指向原链表中的下一个元素 } while (e != null); } } }
HashMap中最常用的hash(int h)函数解析
转:
当系统决定存储HashMap中的key-value对时,完全没有考虑Entry中的value,仅仅只是根据key来计算并决定每个Entry的存储位置。我们完全可以把 Map 集合中的 value 当成 key 的附属,当系统决定了 key 的存储位置之后,value 随之保存在那里即可。
hash(int h)方法根据key的hashCode重新计算一次散列---对每一个key-value的key.hashCode()进行二次hash,为什么?因为indexFor定位时,只用到了后几位hashCode的低位决定,而高位被统统屏蔽了,因此采用防御性编程,旋转Hash函数的主要目的是让原有hashCode的高位信息也能被充分利用,此算法加入了高位计算,防止低位不变,高位变化时,造成的hash冲突,且兼顾计算效率以及数据统计的特性。
/** * Applies a supplemental hash function to a given hashCode, which * defends against poor quality hash functions. This is critical * because HashMap uses power-of-two length hash tables, that * otherwise encounter collisions for hashCodes that do not differ * in lower bits. Note: Null keys always map to hash 0, thus index 0. */ static int hash(int h) { // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }