LinkedHashMap源码分析

之前介绍过HashMap,HashMap的存储是无序的,LinkedHashMap在HashMap的基础上记录了每次添加的顺序,LinkedHashMap总体上实现方法大都是HashMap的方法。其实现方式,是数组+单向链表+双向链表+红黑树

我们先来看下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);
        }
    }

这个Entry对象继承了HashMap.Node对象,同时又添加了前后节点,实现LinkedHashMap的顺序,在每次添加一个元素时,都会将新增元素放在Entry的最后,通知将原tail的after指向新添加的元素,新添加元素的before指向原来的tail, next是用来记录hash值计算出来的桶的位置相同时,将新添加元素放在桶中链表的最后。

在添加元素时:

如果新添加一个Node元素

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); //创建一个新的Node节点,这一步和HashMap相同
        linkNodeLast(p);  //LinkedHashMap需要关联好添加元素的前后关系
        return p;
    }
 private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail; //获取到最后一个添加的元素
        tail = p;  //设定当前元素为tail
        if (last == null)  //如果tail等于null,说明head也是null, 将当前元素设置成head
            head = p;
        else {  
            p.before = last; //将原来的tail设置成新添加元素的before
            last.after = p; //将tail的after设置成新添加元素,
        }
    }

这一步主要是按照添加元素的顺序建立好双向链表的关联关系。

接下来我们来看afterNodeAccess方法,这个方法会在每次更新节点,get查询某个节点之后调用

 void afterNodeAccess(Node<K,V> e) { // 将当前节点移动至最后
        LinkedHashMap.Entry<K,V> last;
        if (accessOrder && (last = tail) != e) { //如果当前节点已经是tail了,就不需要移动了
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.after = null; //
            if (b == null) //如果当前节点的前一个节点为null,说明当前节点是head节点,移动之后,将head节点设置成当前节点的后面节点
                head = a;
            else
                b.after = a; // 将前一个节点的后置节点设置成当前节点后面的节点
            if (a != null)  // 如果当前节点后面的节点不为null,
                a.before = b; // 将当前节点的前置节点设置成当前节点的原前置节点
            else
                last = b; //否则将tail设置成当前节点的前面的节点
            if (last == null)  //如果tail是null,将head设置成当前节点的前一个节点
                head = p;
            else {
                p.before = last; // 否则把当前节点前面的节点设置成tail
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }

如果我们之前设定了accessOrder为true,那么每次就会重新更新一下链表的顺序,这个方法主要是将当前操作的元素,原来维护的双向链表关系修改为前后链表双向关联,当前元素和tail元素双向关联,1,2,3,4,5,6为插入元素时顺序


关于判断是否包含某个value的值,LinkedHashMap的处理方式

 public boolean containsValue(Object value) {
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
            V v = e.value;
            if (v == value || (value != null && value.equals(v)))
                return true;
        }
        return false;
    }

可以看出来是从head元素一直遍历到tail,直到找到value相同的,而HashMap的遍历方式,先从Table最外层开始遍历,然后从链表当中遍历,直到找到value相同的值

对于双向链表,如果要移除一个元素,那么双向链表之间维护的关系也需要重新修改

  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; //将当前需要移除元素的前后关联都清空,gc回收数据
        if (b == null) //如果当前元素的前面元素等于null
            head = a;//说明当前删除元素是head,需要把head改为当前元素后面的元素
        else 
            b.after = a; // 将前面的节点的后置节点设置原后置节点
        if (a == null)
            tail = b;
        else
            a.before = b;
    }

这个方法主要是,将删除元素的双向关联关系去除掉



在遍历LinkedHashMap时,取出来的顺序就是我们插入进去的顺序。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值