JDK容器學習之LinkedHashMap(二):迭代遍歷的實現方式

LinkedHashMap 如何保障有序的遍歷

前一篇《JDK容器學習之LinkedHashMap (一):底層存儲結構分析》 中介紹了LinkedHashMap繼承自HashMap,且內部維護一個雙向鏈表,那麼其遍歷方式是否就是對這個雙向鏈表的遍歷呢?java

1. 根據Map.Entry進行遍歷

public Set<Map.Entry<K,V>> entrySet() {
    Set<Map.Entry<K,V>> es;
    return (es = entrySet) == null ? 
      (entrySet = new LinkedEntrySet()) : es;
}

final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
    public final int size() 
        { return size; }
    public final void clear() 
      { LinkedHashMap.this.clear(); }
    public final Iterator<Map.Entry<K,V>> iterator() {
        return new LinkedEntryIterator();
    }
    public final boolean contains(Object o) {
       // ...
    }
    public final boolean remove(Object o) {
       // ...
    }
    public final Spliterator<Map.Entry<K,V>> spliterator() {
        // ...
    }
    public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
        // ...
    }
}


final class LinkedEntryIterator extends LinkedHashIterator
    implements Iterator<Map.Entry<K,V>> {
    public final Map.Entry<K,V> next() { return nextNode(); }
}

上面給出關鍵的鏈路,entrySet方法調用,首次會建立一個LinkedEntrySet, 內部實現迭代器 LinkedEntryIterator數據結構

因此迭代的主要邏輯就是LinkedEntryIterator的實現方式了學習

abstract class LinkedHashIterator {
    LinkedHashMap.Entry<K,V> next;
    LinkedHashMap.Entry<K,V> current;
    int expectedModCount;

    LinkedHashIterator() {
        // 保證從鏈表的頭開始掃描
        next = head;
        expectedModCount = modCount;
        current = null;
    }

    public final boolean hasNext() {
        return next != null;
    }

    final LinkedHashMap.Entry<K,V> nextNode() {
        LinkedHashMap.Entry<K,V> e = next;
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        if (e == null)
            throw new NoSuchElementException();
        current = e;
        next = e.after;
        return e;
    }

    public final void remove() {
        Node<K,V> p = current;
        if (p == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        current = null;
        K key = p.key;
        removeNode(hash(key), key, null, false, false);
        expectedModCount = modCount;
    }
}

迭代器的實現也比較清楚,首先是在構造器中,將next指向雙向鏈表的頭headthis

nextNode方法中,返回next節點,並將next指向鏈表的下一個節點.net

2. 根據keys進行遍歷

public Set<K> keySet() {
    Set<K> ks;
    return (ks = keySet) == null ? (keySet = new LinkedKeySet()) : ks;
}

final class LinkedKeySet extends AbstractSet<K> {
    public final int size()  
      { return size; }
    public final void clear()  
      { LinkedHashMap.this.clear(); }
    public final Iterator<K> iterator() {
        return new LinkedKeyIterator();
    }
    public final boolean contains(Object o)
      { return containsKey(o); }
    public final boolean remove(Object key) {
        return removeNode(hash(key), key, null, false, true) != null;
    }
    public final Spliterator<K> spliterator()  {
      // xxx
    }
    public final void forEach(Consumer<? super K> action) {
        // xxx
    }
}


final class LinkedKeyIterator extends LinkedHashIterator
    implements Iterator<K> {
    public final K next() { return nextNode().getKey(); }
}

從上面的調用鏈來看,迭代邏輯和上面同樣,惟一的區別是根據key進行迭代時,迭代器的next()方法直接返回Node節點的key,而以前是返回整個Node節點code

3. 遍歷values

基本邏輯同上,省略blog

小結

從遍歷的邏輯來看,LinkedHashMap的遍歷實際上就是遍歷內部維護的雙向鏈表繼承

相關博文

關注更多

掃一掃二維碼,關注小灰灰blogrem

https://static.oschina.net/uploads/img/201709/22221611_Fdo5.jpg

相關文章
相關標籤/搜索