HashMap 大家知道,索引是(length-1) & hash 算出來的,是無序的,那么LinkedHashList是如何保證順序的呢?
答案就是LInkedHashMap的一個內部類,可以看到這個是一個雙向列表,那下個問題,是如何維護呢?
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
那想想之前的HashMap里有一些未實現的方法
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }
看名稱其實就是在訪問,插入和刪除后的操作,這個其實就是為了LInkedHashMap維護鏈表而調用的,如果沒有這些方法,LInkedHashMap就要重寫putVal方法,這很沒必要。挑一個方法來看。
void afterNodeRemoval(Node<K,V> e) { // unlink
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
將該位置的節點前驅和后繼節點置為空
p.before = p.after = null;
// 前驅節點為空,那么該節點的后繼節點就為head
if (b == null)
head = a;
// 不為空,前驅節點的后繼節點為該節點的后繼節點
else
b.after = a;
// 后繼節點為空,尾巴就是前驅節點
if (a == null)
tail = b;
//不為空,則后繼節點的前驅節點,為該節點的前驅節點
else
a.before = b;
}
接下來就是foreach遍歷了,可以看下和LInkedHashMap是重寫了這個方法的,和HashMap的不同
//HashMap:
public void forEach(BiConsumer<? super K, ? super V> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
// 是用tab循環
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key, e.value);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
//LInkedHashMap
public void forEach(BiConsumer<? super K, ? super V> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
//遍歷的是這個雙向鏈表,從head開始,所以是有序的,就是插入順序
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
action.accept(e.key, e.value);
if (modCount != mc)
throw new ConcurrentModificationException();
}