HashMap中定义了一个Node数组table用来存储元素。
transient Node<K,V>[] table;
如果调用HashMap的无参构造器创建HashMap对象,一开始,HashMap不会实例化table数组。只有第一次调用put(K key, V value)方法时,HashMap才会table创建一个初始容量为16的Node<K,V>[]。
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
如果当插入的键值对数量大于threshold值时,HashMap为会table数组扩容成原来容量的2倍。其中的threshold=(int)(DEFAULT_LOAD_FACTOR * table.length),也就是说,当键值对数量大于装载因子与table数组长度的乘积时,HashMap才会扩容。下面是具体扩容方法。
final Node<K, V>[] resize() { Node<K, V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float) newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ? (int) ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({ "rawtypes", "unchecked" }) Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K, V> e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode<K, V>) e).split(this, newTab, j, oldCap); else { // preserve order Node<K, V> loHead = null, loTail = null; Node<K, V> hiHead = null, hiTail = null; Node<K, V> next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
那么HashMap是怎样通过插入的key值的hashcode确定其在table数组中的位置呢?
首先对key的hashcode值无符号右移16位与其hashcode值进行异或运算,具体计算过程如下:static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
然后在用上一过程得到的新hash值和table.length-1做&运算得到其在table中的位置,如果此位置上为null,也就是没有元素,则直接新建一个Node存储在该位置上。
if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null);
如果此位置上不为空分为以下几种情况
1.此位置上的节点不是TreeNode类型,并且插入元素的key值equals.(此节点的key值)为true时,用传入进来的value值覆盖原有的value值。
if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k)))) e = p;//先将原节点暂存到e中 //覆盖 if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value;//用新value值将原value值覆盖 afterNodeAccess(e); return oldValue;
2.此位置上的节点不是TreeNode类型,并且插入元素的key值equals.(此节点的key值)为false时,那么以此结点为头结点,向下寻找子结点,如果子结点的key值equals.(key值)为true时,同样用新value值将原来结点处的value值覆盖,否则直到子结点的next值为null时,新建一个Node存储在next位置。之后判断该链表的长度是否大于等于8,如果成立,则调用treeifyBin(Node<K,V>[] tab, int hash)将此链表树化。
else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); 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;//执行到此处时,e所只向的节点为将要被覆盖的节点 p = e; } //覆盖 if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value;//用新value值将原value值覆盖 afterNodeAccess(e); return oldValue;
3.如果此位置上的节点是TreeNode类型,则调用putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,int h, K k, V v)方法将其插入树中。
else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
大家可能会好奇为什么HashMap.Node类型的数组能存储HashMap.TreeNode类型呢,其实HashMap.TreeNode是HashMap.Node的间接子类,HashMap.TreeNode继承了LinkedHashMap.Entry类,而LinkedHashMap.Entry又继承了HashMap.Node类。所以table数组中能存储HashMap.TreeNode类型。