从头结点开始向后遍历,找到当前链表的最后几个下标相同的连续的节点。如上图,虽然开头出现了有两个节点的下标都是 k2, 但是中间出现一个不同的下标 k1,打断了下标连续相同,因此从下一个k2,又重新开始算。好在后边三个连续的节点下标都是相同的,因此倒数第三个节点被标记为 lastRun,且变量无变化。
从lastRun节点到尾结点的这部分就可以整体迁移到新数组的对应下标位置了,因为它们的下标都是相同的,可以这样统一处理。
另外从头结点到 lastRun 之前的节点,无法统一处理,只能一个一个去复制了。且注意,这里不是直接迁移,而是复制节点到新的数组,旧的节点会在不久的将来,因为没有引用指向,被 JVM 垃圾回收处理掉。
(不知道为啥这个方法名起为 rehash,其实扩容时 hash 值并没有重新计算,变化的只是它们所在的下标而已。我猜测,可能是,借用了 1.7 HashMap 中的说法吧。。。)
get()put 方法搞明白了之后,其实 get 方法就很好理解了。也是先定位到 Segment,然后再定位到 HashEntry 。
public V get(Object key) { Segment<K,V> s; // manually integrate access methods to reduce overhead HashEntry<K,V>[] tab; //计算hash值 int h = hash(key); //同样的先定位到 key 所在的Segment ,然后从主内存中取出最新的节点 long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null && (tab = s.table) != null) { //若Segment不为空,且链表也不为空,则遍历查找节点 for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); e != null; e = e.next) { K k; //找到则返回它的 value 值,否则返回 null if ((k = e.key) == key || (e.hash == h && key.equals(k))) return e.value; } } return null; } remove()remove 方法和 put 方法类似,也不用做过多特殊的介绍,
public V remove(Object key) { int hash = hash(key); //定位到Segment Segment<K,V> s = segmentForHash(hash); //若 s为空,则返回 null,否则执行 remove return s == null ? null : s.remove(key, hash, null); } public boolean remove(Object key, Object value) { int hash = hash(key); Segment<K,V> s; return value != null && (s = segmentForHash(hash)) != null && s.remove(key, hash, value) != null; } final V remove(Object key, int hash, Object value) { //尝试加锁,若失败,则执行 scanAndLock ,此方法和 scanAndLockForPut 方法类似 if (!tryLock()) scanAndLock(key, hash); V oldValue = null; try { HashEntry<K,V>[] tab = table; int index = (tab.length - 1) & hash; //从主内存中获取对应 table 的最新的头结点 HashEntry<K,V> e = entryAt(tab, index); HashEntry<K,V> pred = null; while (e != null) { K k; HashEntry<K,V> next = e.next; //匹配到 key if ((k = e.key) == key || (e.hash == hash && key.equals(k))) { V v = e.value; // value 为空,或者 value 也匹配成功 if (value == null || value == v || value.equals(v)) { if (pred == null) setEntryAt(tab, index, next); else pred.setNext(next); ++modCount; --count; oldValue = v; } break; } pred = e; e = next; } } finally { unlock(); } return oldValue; } size()