博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LinkedHashMap源码分析
阅读量:4149 次
发布时间:2019-05-25

本文共 3416 字,大约阅读时间需要 11 分钟。

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

我们先来看下LinkedHashMap保存元素的结构

static class Entry
extends HashMap.Node
{ Entry
before, after; Entry(int hash, K key, V value, Node
next) { super(hash, key, value, next); } }

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

在添加元素时:

如果新添加一个Node元素

Node
newNode(int hash, K key, V value, Node
e) { LinkedHashMap.Entry
p = new LinkedHashMap.Entry
(hash, key, value, e); //创建一个新的Node节点,这一步和HashMap相同 linkNodeLast(p); //LinkedHashMap需要关联好添加元素的前后关系 return p; }
private void linkNodeLast(LinkedHashMap.Entry
p) { LinkedHashMap.Entry
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
e) { // 将当前节点移动至最后 LinkedHashMap.Entry
last; if (accessOrder && (last = tail) != e) { //如果当前节点已经是tail了,就不需要移动了 LinkedHashMap.Entry
p = (LinkedHashMap.Entry
)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
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
e) { // unlink LinkedHashMap.Entry
p = (LinkedHashMap.Entry
)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时,取出来的顺序就是我们插入进去的顺序。

你可能感兴趣的文章
OpenFeign学习(三):OpenFeign配置生成代理对象
查看>>
OpenFeign学习(四):OpenFeign的方法同步请求执行
查看>>
OpenFeign学习(六):OpenFign进行表单提交参数或传输文件
查看>>
Ribbon 学习(二):Spring Cloud Ribbon 加载配置原理
查看>>
Ribbon 学习(三):RestTemplate 请求负载流程解析
查看>>
深入理解HashMap
查看>>
XML生成(一):DOM生成XML
查看>>
XML生成(三):JDOM生成
查看>>
Ubuntu Could not open lock file /var/lib/dpkg/lock - open (13:Permission denied)
查看>>
collect2: ld returned 1 exit status
查看>>
C#入门
查看>>
C#中ColorDialog需点两次确定才会退出的问题
查看>>
数据库
查看>>
nginx反代 499 502 bad gateway 和timeout
查看>>
linux虚拟机安装tar.gz版jdk步骤详解
查看>>
python实现100以内自然数之和,偶数之和
查看>>
去哪儿一面+平安科技二面+hr面+贝贝一面+二面产品面经
查看>>
pytorch
查看>>
pytorch(三)
查看>>
C++ 调用json
查看>>