LinkedHashMap源码学习
程序员文章站
2023-11-18 13:06:22
描述 可以按照添 加元素的顺序 对元素进行迭代的 的子类. 注意,上面说的是 加元素的顺序 .也就是说, 更新元素 时,是不会影响遍历结构的的.除非设置参数 为`true`,将更新元素放置到 队末 . 这个类没有对其父类 进行过多重写.主要通过实现 相关方法,在数据结构变更后,进行后置的 结构更新进 ......
描述
- 可以按照添加元素的顺序对元素进行迭代的
hashmap
的子类. - 注意,上面说的是加元素的顺序.也就是说,更新元素时,是不会影响遍历结构的的.除非设置参数
accessorder
为true
,将更新元素放置到队末. - 这个类没有对其父类
hashmap
进行过多重写.主要通过实现afternode*
相关方法,在数据结构变更后,进行后置的链表
结构更新进行维护.
常用与关键方法
linknodelast
方法
描述:
- 负责初始化成员变量
head
与tail
. - 当
head
与tail
初始化完成后,负责将目标元素p
连接到tail
并更新原有tail
到目标元素p
代码:
private void linknodelast(linkedhashmap.entry<k,v> p) { // 缓存尾部 linkedhashmap.entry<k,v> last = tail; // 更新尾部到新元素 tail = p; // 判断老尾部是否已经初始化 if (last == null) // 老尾部为初始化,代表头部也没初始化.进行初始化操作 head = p; else { // 初始化以完成,将p链接到老尾部之后 p.before = last; last.after = p; } }
transferlinks
方法
描述:
使用dst
替换src
在双向链表中的位置
代码:
private void transferlinks(linkedhashmap.entry<k,v> src, linkedhashmap.entry<k,v> dst) { // 同步before,同时保存到局部变量 linkedhashmap.entry<k,v> b = dst.before = src.before; // 同步after,同时保存到局部变量 linkedhashmap.entry<k,v> a = dst.after = src.after; // 检查before if (b == null) // 没有before.将dst设置为head节点 head = dst; else // 有before,将before与dst关联 b.after = dst; // 检查after if (a == null) // 没有after,将dst作为tail节点 tail = dst; else // 有after,将after与dst连接 a.before = dst; }
newnode
方法
描述:
重写了父类newnode
方法.扩展双向链表的连接操作.返回了hashmap.node
的子类节点linkedhashmap.entry
.
代码:
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; }
replacementnode
方法
描述:
扩展双向链表替换节点的操作.这个方法用于父类hashmap
将hashmap.treenode
替换为hashmap.node
时调用,这里进行了重写,使用带有双向链表的linkedhashmap.entry
作为返回值
注意: 这里hashmap.treenode
是实现了linkedhashmap.entry
的.也就是参数p
,他可以直接强转为实现类linkedhashmap.entry
代码:
node<k,v> replacementnode(node<k,v> p, node<k,v> next) { linkedhashmap.entry<k,v> q = (linkedhashmap.entry<k,v>)p; linkedhashmap.entry<k,v> t = new linkedhashmap.entry<k,v>(q.hash, q.key, q.value, next); // 替换节点 transferlinks(q, t); return t; }
newtreenode
方法
描述:
重写了父类方法newtreenode
.扩展双向链表的连接操作.同样,因为hashmap.treenode
实现linkedhashmap.entry
.可以直接通过linknodelast
方法进行连接操作
代码:
treenode<k,v> newtreenode(int hash, k key, v value, node<k,v> next) { treenode<k,v> p = new treenode<k,v>(hash, key, value, next); linknodelast(p); return p; }
replacementtreenode
方法
描述:
同replacementnode
.扩展双向链表替换节点的操作.只是节点类型变成了treenode
.又因为他是linkedhashmap.entry
的子类,可以直接交给transferlinks
使用.进行双向链表替换操作
代码:
treenode<k,v> replacementtreenode(node<k,v> p, node<k,v> next) { linkedhashmap.entry<k,v> q = (linkedhashmap.entry<k,v>)p; treenode<k,v> t = new treenode<k,v>(q.hash, q.key, q.value, next); transferlinks(q, t); return t; }
afternoderemoval
方法
描述:
删除节点后调用.进行双向链表同步
代码:
void afternoderemoval(node<k,v> e) { // unlink // b - before节点 // p - 被删除节点 // a - after节点 linkedhashmap.entry<k,v> p = (linkedhashmap.entry<k,v>)e, b = p.before, a = p.after; // 清除p的双端引用 p.before = p.after = null; // 判断before是否存在 if (b == null) // 没有before // 设置a为head head = a; else // 存在before // 连接b->a.注意,这是单向连接,现在还无法确认a是否存在.如果a为空,b就是链表中的唯一节点.after属性为null b.after = a; // 判断a是否为空 if (a == null) // a为空 // tail设置为b tail = b; else // a存在 // 连接 a->b.注意,这里也是单向连接.如果b是空的话,a现在就是head且before属性是null a.before = b; }
afternodeaccess
方法
描述:
更新节点后调用.进行双向链表同步
代码:
void afternodeaccess(node<k,v> e) { // move node to last // oldtail.老尾部缓存 linkedhashmap.entry<k,v> last; // 判断accessorder.即按照访问(更新)顺序排列 // 获取老尾部 // 判断当前元素是不是尾部元素 if (accessorder && (last = tail) != e) { // accessorder==true且e不要尾部元素 // b - fefore // p - 当前元素 // a - after linkedhashmap.entry<k,v> p = (linkedhashmap.entry<k,v>)e, b = p.before, a = p.after; // 因为p将变为尾部元素,所以直接设置p.after为null. p.after = null; // 判断b if (b == null) // b为null,p节点就是head节点 // a作为头部节点 head = a; else // b不为空 // 连接b->a. 注意,这里是单向连接.a可能为null,a.before的连接交给后续判断 b.after = a; // 判断a if (a != null) // a不为空 // a->b.注意,这里是单向链接.b可能是null.b.after的连接交给后续判断 a.before = b; else // a为空.p节点就是tail节点 // 这里有两个分支,需要判断b是否为空.此处a已经为空,如果b也为空,说明p是列表中的唯一节点.这个判断委托到后续判断中处理 // 此时,last变量已经失去意义,它与p为同一对象. // 这里说一下赋值last = b;的作用.注意,这是本人猜测! // 是为了统一算法的外在样式.因为变量last在在本方法中是不会为空的,且在所有的情形中,都会调用p.before = last;last.after = p;进行连接(除了p是唯一元素的情况). // 那么在b存在的时候,再次与p进行连接,在链表结构上也是没有问题的,统一被视作被操作元素的前一个元素 last = b; if (last == null) // p是唯一元素 head = p; else { // 连接到尾部节点 p.before = last; last.after = p; } // 更新尾部节点到p tail = p; // 修改计数++ ++modcount; } }
内部类
linkedhashiterator
描述:
封装了针对链表结构的迭代器.并向子类提供了共有的扩展方法.
代码:
abstract class linkedhashiterator { linkedhashmap.entry<k,v> next; linkedhashmap.entry<k,v> current; int expectedmodcount; linkedhashiterator() { // 初始化next节点为当前head next = head; expectedmodcount = modcount; current = null; } public final boolean hasnext() { return next != null; } final linkedhashmap.entry<k,v> nextnode() { // 缓存next linkedhashmap.entry<k,v> e = next; // fast-fail if (modcount != expectedmodcount) throw new concurrentmodificationexception(); // next为空 if (e == null) throw new nosuchelementexception(); // 设置当前 current = e; // 更新next到下一个 next = e.after; return e; } public final void remove() { // 获取当前 node<k,v> p = current; // null判断 if (p == null) throw new illegalstateexception(); // fast-fail if (modcount != expectedmodcount) throw new concurrentmodificationexception(); // 迭代器置空 current = null; // 获取key k key = p.key; // 调用父类的removenode方法进行节点删除 removenode(hash(key), key, null, false, false); // 同步更新计数 expectedmodcount = modcount; } }
内部类
linkedhashiterator实现
描述:
分别继承了linkedhashiterator
并使用前者的nextnode
方法返回不同数据
代码:
final class linkedkeyiterator extends linkedhashiterator implements iterator<k> { public final k next() { return nextnode().getkey(); } } final class linkedvalueiterator extends linkedhashiterator implements iterator<v> { public final v next() { return nextnode().value; } } final class linkedentryiterator extends linkedhashiterator implements iterator<map.entry<k,v>> { public final map.entry<k,v> next() { return nextnode(); } }
上一篇: 谈谈EF Core实现数据库迁移