作者:透明 | 来源:互联网 | 2023-10-13 10:44
这是一道阿里的面试题,考察你对HashMap源码的了解情况,废话不多说,咱们就直接上源码吧!
jdk 1.7 源码 void resize ( int newCapacity) { Entry[ ] oldTable &#61; table; int oldCapacity &#61; oldTable. length; if ( oldCapacity &#61;&#61; MAXIMUM_CAPACITY) { threshold &#61; Integer. MAX_VALUE; return ; } Entry[ ] newTable &#61; new Entry [ newCapacity] ; boolean oldAltHashing &#61; useAltHashing; useAltHashing |&#61; sun. misc. VM. isBooted ( ) && ( newCapacity >&#61; Holder. ALTERNATIVE_HASHING_THRESHOLD) ; boolean rehash &#61; oldAltHashing ^ useAltHashing; transfer ( newTable, rehash) ; table &#61; newTable; threshold &#61; ( int ) Math. min ( newCapacity * loadFactor, MAXIMUM_CAPACITY &#43; 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; else loTail. next &#61; e; loTail &#61; e; } else { if ( hiTail &#61;&#61; null) hiHead &#61; e; else hiTail. 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不会倒置