LinkedHashMap源码(jdk1.8)

LinkedHashMap继承自HashMap,建议先学习HashMap源码,然后再学习LinkedHashMap源码,HashMap源码,这样会快很多。

LinkedHashMap继承了HashMap,所以大体上是仍然是HashMap的结构,但是在内部实现增加两个指针来构成一个双向链表来维持顺序,同时提供了两种可选择的顺序。LinkedHashMap与HashMap的面向对象设计思想很值得学习,其次LinkedHashMap在需要有序Map时候是一种选择,也为缓存的实现提供一种基础组件。
LinkedHashMap相比于HashMap主要重写了newNode(), afterNodeRemoval(), afterNodeInsertion(), afterNodeAccess()几个重要方法,增加了head,tail,accessOrder三个内部字段。

  1. 基本字段
transient LinkedHashMap.Entry head; //双链表的头指针

transient LinkedHashMap.Entry tail; //双链表的尾指针

/**
    * accesssOrder为true表示linkedHashMap的顺序是按照访问顺序
    * 就是会将最新被访问过的节点移到链表末尾
    * 比如1,2,3三个结点,如果访问了一次2,就会变成1,3,2
    * 为false表示顺序是按照插入顺序来保持,后续不会更改
    * 默认值为false
*/
final boolean accessOrder;
  1. 构造函数
public LinkedHashMap(int initialCapacity, float loadFactor)
public LinkedHashMap(int initialCapacity)
public LinkedHashMap()
public LinkedHashMap(Map m)
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)

基本实现是调用父类的构造函数,然后给accessOrder赋值

  1. class Entry (LinkedHashMap如何维持顺序的)
static class Entry extends HashMap.Node {
        Entry before, after;
        Entry(int hash, K key, V value, Node next) {
            super(hash, key, value, next);
        }
}

LinkedHashMap的Entry类继承自HashMap的Node类,但是增加了before,after两个字段来构成一个双向链表,通过这两个字段来维持结点的顺序,在put,remove,get之后都会对链表进行相应的调整。

  1. newNode()函数

LinkedHashMap没有重写HashMap的put和putVal方法,如何将节点连到链表上的,就是通过newNode()函数,因为putVal的时候,一定会newNode,在newNode的时候就将节点连接到到链表上,这样就避免了putVal方法的重写,大量代码重复。

    /**
     * 只重写了这个方法,没有重写put方法,因为put的时候会调用这个方法
     * 在这里把最新的结点插入到链表的末尾来维持顺序
     */
    Node newNode(int hash, K key, V value, Node e) {
        LinkedHashMap.Entry p =
            new LinkedHashMap.Entry(hash, key, value, e);
        linkNodeLast(p);
        return p;
    }
    /**
     * 双向链表的基本操作
     */
    private void linkNodeLast(LinkedHashMap.Entry p) {
        LinkedHashMap.Entry last = tail;
        tail = p;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }
  1. afterNodeRemoval函数
    /**
     * 删除结点的时候,同时在链表中删除
     *  链表的基本操作应该都能看懂
     */
    void afterNodeRemoval(Node e) { // unlink
        LinkedHashMap.Entry p =
            (LinkedHashMap.Entry)e, b = p.before, a = p.after;
        p.before = p.after = null;
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a == null)
            tail = b;
        else
            a.before = b;
    }
  1. afterNodeAccess函数
    /**
     * 将一个结点移动到链表的末尾,配合accessOrder使用
     */
    void afterNodeAccess(Node e) { // move node to last
        LinkedHashMap.Entry last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry p =
                (LinkedHashMap.Entry)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;
        }
    }
  1. afterNodeInsertion函数

这个函数比较有趣,在HashMap的putVal中就调用了他,但是是一个空方法;在LinkedHashMap中又调用了他,但是基本还是个空方法;这个方法在ExpiringCache这个类中才发挥了作用。

    /**
     * 这个方法在正常的LinkedHashMap方法中是不会被调用的
     * 因为removeEldestEntry()返回始终为false
     */
    void afterNodeInsertion(boolean evict) { // possibly remove eldest
        LinkedHashMap.Entry first;
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }

看removeEldestEntry函数

    /**
     * 在LinkedHashMap中始终返回的是false
     * 作用是为LinkedHashMap做缓存提供一个实现
     * 比如ExpiringCache,只需要重写这个方法
     * 在进行缓存淘汰的时候可以发挥作用
     * 比如用LinkedHash实现一个LRU算法
     */
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return false;
    }

这个方法在ExpiringCache被重写了,是这样的:

    ExpiringCache(long millisUntilExpiration) {
        this.millisUntilExpiration = millisUntilExpiration;
        map = new LinkedHashMap() {
            protected boolean removeEldestEntry(Map.Entry eldest) {
              return size() > MAX_ENTRIES; //当缓存满的时候,淘汰一个最老的缓存
            }
          };
    }

所以jdk是将LinkedHashMap作为了一种缓存的实现组件来设计,然后空出缓存具体部分由用户实现,我想通过读LinkedHashMap源码,应该很容易实现一个LRU算法了吧。

你可能感兴趣的:(Java基础)