JDK7 and JDK8 HashMap implementation in a lot of people do not understand, you obtain comprehensive yet?

The JDK7 hashMap

HashMap underlying maintain an array, the array Entry Each entry is a

transient Entry<K,V>[] table;


We placed into a HashMap object is actually stored in the array of them;

Map of the key, value stored in places in the form of an array Entry

static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; int hash;


Entry and this should be placed on which position on the array (this position is generally referred to as bit hash bucket or bucket, i.e., the same hash value will be placed in the same position Entry, connected by chain), is calculated by the hashCode key.

final int hash(Object k) { int h = 0; h ^= k.hashCode(); h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }


Calculated by the hash value will be used indexFor way to find table index where it should be:

static int indexFor(int h, int length) { return h & (length-1); }


This method is the equivalent of modulo table.length.

When the two are the same key by hashCode calculation, a hash conflict occurs (collision), the HashMap resolve conflicts with hash chain.

When a hash collision occurs, will be stored in the array is set to Entry next new value (here to be noted that, after such A and B are mapped to the hash index i in A have the previously, when a map when .put (B), the index i into B, a is compared with the B next, the new value is stored in the array, the value of the old value in the new list)

schematic diagram:


JDK7 and JDK8 HashMap implementation in a lot of people do not understand, you obtain comprehensive yet?




So when a lot of hash conflict, HashMap degenerate into a linked list.

To sum up process after map.put:

When put to the HashMap one pair of keys, it is calculated according to the value of key hashCode a position which is the position of this unit is ready to store in the array.

If the position of the object does not exist, among this object directly into the array; if the position of the object already exists, the presence of this object down chain starts looking (whether the value of the same order to determine whether, map allowed <key , value> pair repeat key), if there is an object on the chain, then, again using the equals method compared, if the equals method of each object on this chain are comparison is false, then the object into an array which and then an array of objects that previously existed to link to that position behind the object.

Notably, when the key is null, are placed in table [0] in

private V putForNullKey(V value) { for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(0, null, value, 0); return null; }


When the size is greater than the threshold, expansion occurs. threshold equal capacity * load factor

void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); }


jdk7中resize,只有当 size>=threshold并且 table中的那个槽中已经有Entry时,才会发生resize。即有可能虽然size>=threshold,但是必须等到每个槽都至少有一个Entry时,才会扩容。还有注意每次resize都会扩大一倍容量

JDK8中的HashMap

一直到JDK7为止,HashMap的结构都是这么简单,基于一个数组以及多个链表的实现,hash值冲突的时候,就将对应节点以链表的形式存储。

这样子的HashMap性能上就抱有一定疑问,如果说成百上千个节点在hash时发生碰撞,存储一个链表中,那么如果要查找其中一个节点,那就不可避免的花费O(N)的查找时间,这将是多么大的性能损失。这个问题终于在JDK8中得到了解决。再最坏的情况下,链表查找的时间复杂度为O(n),而红黑树一直是O(logn),这样会提升 HashMap的效率。

JDK7中HashMap采用的是位桶+链表的方式,即我们常说的散列链表的方式,而JDK8中采用的是位桶+链表/红黑树(有关红黑树请查看红黑树)的方式,也是非线程安全的。当某个位桶的链表的长度达到某个阀值的时候,这个链表就将转换成红黑树。


JDK7 and JDK8 HashMap implementation in a lot of people do not understand, you obtain comprehensive yet?




JDK8中,当同一个hash值的节点数不小于8时,将不再以单链表的形式存储了,会被调整成一颗红黑树(上图中null节点没画)。这就是JDK7与JDK8中HashMap实现的最大区别。

接下来,我们来看下JDK8中HashMap的源码实现。

JDK中Entry的名字变成了Node,原因是和红黑树的实现TreeNode相关联。

transient Node<K,V>[] table;


当冲突节点数不小于8-1时,转换成红黑树。

static final int TREEIFY_THRESHOLD = 8;


以put方法在JDK8中有了很大的改变

public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; //如果当前map中无数据,执行resize方法。并且返回n if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; //如果要插入的键值对要存放的这个位置刚好没有元素,那么把他封装成Node对象,放在这个位置上就完事了 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); //否则的话,说明这上面有元素 else { Node<K,V> e; K k; //如果这个元素的key与要插入的一样,那么就替换一下,也完事。 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; //1.如果当前节点是TreeNode类型的数据,执行putTreeVal方法 else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { //还是遍历这条链子上的数据,跟jdk7没什么区别 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); //2.完成了操作后多做了一件事情,判断,并且可能执行treeifyBin方法 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) //true || -- e.value = value; //3. afterNodeAccess(e); return oldValue; } } ++modCount; //判断阈值,决定是否扩容 if (++size > threshold) resize(); //4. afterNodeInsertion(evict); return null; }


treeifyBin () is converted into a red-black tree list.

Before indefFor () method disappeared, the direct use (tab.length-1) & hash, so to see this, is represented by the subscript array.

static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

Like this article, you can point to the author likes to point at attention, will share Java-related articles every day!

Remember to focus on me oh, will occasionally presented benefits, including consolidation of interview questions, learning materials, source code, etc. ~ ~


Guess you like

Origin blog.51cto.com/14456091/2432620