Java集合框架——LinkedHashMap源码分析

LinkedHashMap 继承了 HashMap 类。
HashMap 类的源码详细分析参考博客:HashMap源码分析
LinkedHashMap底层仍是数组和链表实现的。但是为什么可以在迭代时,返回和插入顺序相同的顺序呢。
因为它不是用的 HashMap 的单向链表,而是双向链表。通过维护这个双向链表来保证对哈希表迭代时的有序性,即LinkedHashMap 采用的 hash 算法和 HashMap 相同,但是它重新定义了数组中保存的元素 Entry,该 Entry 除了保存当前对象的引用外,还保存了其上一个元素 before 和下一个元素 after 的引用,从而在哈希表的基础上又构成了双向链接列表。

属性

    // 双向链表的头节点
    transient LinkedHashMap.Entry<K,V> head;
    // 双向链表的尾节点
    transient LinkedHashMap.Entry<K,V> tail;
    // 迭代的顺序;true的话按照访问顺序迭代;false则按照插入顺序迭代
    final boolean accessOrder;

节点类

节点类继承自 HashMap 的节点类,因为是双向链表的节点,其保存了上一个和下一个节点的指针。

    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 的构造函数分析

    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }

    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

关键方法

这里可以注意一下 HashMap 中的 put 方法,其中涉及到下边的两个方法。在 HashMap 中,以下的两个方法为空,就是要在 LinkedHashMap 中预留的方法。

afterNodeAccess

在访问顺序的LinkedHashMap中,afterNodeAccess 在节点被访问过后,将访问的元素移到链表的最后一个元素的位置:

    // 方法主要是
    void afterNodeAccess(Node<K,V> e) {
        LinkedHashMap.Entry<K,V> last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.after = null;
            if (b == null)
                head = a;
            else
                b.after = a;
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }

afterNodeInsertion

在新节点插入后的回调,这里removeEldestEntry返回false,即默认不移除头结点即最老的节点。这里可以进行重写该方法,来实现一个简单的LRU(Least Recently Used)Cache,即超出一定缓存后,最老的节点要删掉。

    void afterNodeInsertion(boolean evict) { // possibly remove eldest
        LinkedHashMap.Entry<K,V> first;
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }

    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }
    原文作者:java集合源码分析
    原文地址: https://blog.csdn.net/yx0628/article/details/79331515
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞