作者:透明 | 来源:互联网 | 2023-10-13 10:44
这是一道阿里的面试题,考察你对HashMap源码的了解情况,废话不多说,咱们就直接上源码吧!
jdk 1.7 源码
void resize(int newCapacity) {Entry[] oldTable = table;int oldCapacity = oldTable.length;if (oldCapacity == MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return;}Entry[] newTable = new Entry[newCapacity];boolean oldAltHashing = useAltHashing;useAltHashing |= sun.misc.VM.isBooted() &&(newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);boolean rehash = oldAltHashing ^ useAltHashing;transfer(newTable, rehash);table = newTable;threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
void transfer(Entry[] newTable, boolean rehash) {int newCapacity &#61; newTable.length;for (Entry<K,V> e : table) {while(null !&#61; e) {Entry<K,V> next &#61; e.next;if (rehash) {e.hash &#61; null &#61;&#61; e.key ? 0 : hash(e.key);}int i &#61; indexFor(e.hash, newCapacity);e.next &#61; newTable[i];newTable[i] &#61; e;e &#61; next;}}
}
jdk 1.8 源码(比较长&#xff0c;慢慢品哈)
final Node<K,V>[] resize() {Node<K,V>[] oldTab &#61; table;int oldCap &#61; (oldTab &#61;&#61; null) ? 0 : oldTab.length;int oldThr &#61; threshold;int newCap, newThr &#61; 0;if (oldCap > 0) {if (oldCap >&#61; MAXIMUM_CAPACITY) {threshold &#61; Integer.MAX_VALUE;return oldTab;}else if ((newCap &#61; oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >&#61; DEFAULT_INITIAL_CAPACITY)newThr &#61; oldThr << 1; }else if (oldThr > 0) newCap &#61; oldThr;else {newCap &#61; DEFAULT_INITIAL_CAPACITY;newThr &#61; (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}if (newThr &#61;&#61; 0) {float ft &#61; (float)newCap * loadFactor;newThr &#61; (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}threshold &#61; newThr;&#64;SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[] newTab &#61; (Node<K,V>[])new Node[newCap];table &#61; newTab;if (oldTab !&#61; null) {for (int j &#61; 0; j < oldCap; &#43;&#43;j) {Node<K,V> e;if ((e &#61; oldTab[j]) !&#61; null) {oldTab[j] &#61; null;if (e.next &#61;&#61; null)newTab[e.hash & (newCap - 1)] &#61; e;else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else { Node<K,V> loHead &#61; null, loTail &#61; null;Node<K,V> hiHead &#61; null, hiTail &#61; null;Node<K,V> next;do {next &#61; e.next;if ((e.hash & oldCap) &#61;&#61; 0) {if (loTail &#61;&#61; null)loHead &#61; e;elseloTail.next &#61; e;loTail &#61; e;}else {if (hiTail &#61;&#61; null)hiHead &#61; e;elsehiTail.next &#61; e;hiTail &#61; e;}} while ((e &#61; next) !&#61; null);if (loTail !&#61; null) {loTail.next &#61; null;newTab[j] &#61; loHead;}if (hiTail !&#61; null) {hiTail.next &#61; null;newTab[j &#43; oldCap] &#61; hiHead;}}}}}return newTab;}
总结
jdk1.7扩容是重新计算hash&#xff1b;jdk1.8是要看看原来的hash值新增的那个bit是1还是0好了&#xff0c;如果是0则索引没变&#xff0c;如果是1则索引变成"原索引&#43;oldCap".这是jdk1.8的亮点&#xff0c;设计的确实非常的巧妙&#xff0c;即省去了重新计算hash值得时间&#xff0c;又均匀的把之前的冲突的节点分散到新的数组bucket上
jdk1.7在rehash的时候&#xff0c;旧链表迁移到新链表的时候&#xff0c;如果在新表的数组索引位置相同&#xff0c;则链表元素会倒置&#xff0c;但是jdk1.8不会倒置