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<K,V> head; //双链表的头指针

transient LinkedHashMap.Entry<K,V> 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<? extends K, ? extends V> m)
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)

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

  1. class Entry<K,V> (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);
        }
}

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

  1. newNode()函数

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

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

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

    /**
     * 这个方法在正常的LinkedHashMap方法中是不会被调用的
     * 因为removeEldestEntry()返回始终为false
     */
    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);
        }
    }

看removeEldestEntry函数

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值