LinkedHashMap源码解析(下)

简介: LinkedHashMap维护插入的顺序。

4 按插入顺序访问

LinkedHashMap 默认 accessOrder 为 false,提供按照插入顺序的访问,并没有重写父类 HashMap 的 put 方法.但在 HashMap 中,put 的是 HashMap 的 Node 类型节点,LinkedHashMap 的 Entry 与其结构并不同,又是怎样建立起双向链表的呢?下面一起看下 LinkedHashMap 插入相关代码.


忽略未重写的 put=>putValue代码部分,我们直接观察重写的

newNode

  • HashMap
    image.png
  • LinkedHashMap 重写
    image.png
    控制新增节点追加到链表的尾部,这样每次新节点都追加到尾部,即可保证插入顺序了.
    继续研究 linkNodeLast

linkNodeLast

新增节点,并追加到链表的尾部.

`// link at the end of list`
`private` `void` `linkNodeLast(LinkedHashMap.Entry<K,V> p) {`
`LinkedHashMap.Entry<K,V> last = tail;`
`// 新增于尾节点`
`tail = p;`
`// last 为null,说明链表为空`
`if` `(last == ``null``)`
`head = p;`
`// 链表非空,建立新节点和上一个尾节点的前后关系`
`else` `{`
`// 将新节点 p 直接接在链尾`
`p.before = last;`
`last.after = p;`
`}`
`}`

由此得知,通过在 HashMap 基础上新增的头尾节点,节点的 before 和 after 属性,就能实现在每次新增时,把节点直接追加到尾节点,即可达到维护按照插入顺序的链表结构的目的!

  • 图解链表创建步骤
  • image.png
  • 蓝色部分是 HashMap 的方法

橙色部分为 LinkedHashMap 独有方法

注意 LinkedHashMap 虽然也是双向链表,但只提供单向的按插入的顺序从头到尾访问,不及 LinkedList 般可双向无死角访问.


LinkedHashMap 通过迭代器访问,而且默认是从头节点开始访问

image.png

迭代过程中,不断访问 after 节点即可完成遍历.

image.png

  • 1 处进行校验
    2 处通过节点的 after 属性,找到后继节点

5 链表节点的删除

  • HashMap 中保存的允许 LinkedHashMap 后处理的回调
  • image.png
  • 与插入操作一样,LinkedHashMap 删除操作相关的代码也是直接用父类的实现. 在删除节点时,父类不会修复 LinkedHashMap 的双向链表。那么删除及节点后,被删除的节点该如何从双链表中安全移除呢?其实在删除节点后,回调方法 afterNodeRemoval 会被调用。LinkedHashMap 重写了该方法.
`// e 为已经删除的节点`
`void` `afterNodeRemoval(Node<K,V> e) { ``// unlink`
`LinkedHashMap.Entry<K,V> p =`
`(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;`
`// 将 p 节点的前驱后后继引用置 null,辅助 GC`
`p.before = p.after = ``null``;`
`// p.before 为 null,表明 p 是头节点`
`if` `(b == ``null``)`
`head = a;`
`else`
`// 否则将 p 的前驱节点连接到 p 的后继节点`
`b.after = a;`
`// a 为 null,表明 p 是尾节点`
`if` `(a == ``null``)`
`tail = b;`
`else`
`// 否则将 a 的前驱节点连接到 b`
`a.before = b;`
`}`

删除元素的主要流程:

  1. 根据 hash 定位到桶位置
  2. 遍历链表或调用红黑树相关的删除方法
  3. 从 LinkedHashMap 维护的双链表中移除要删除的节点

image.png

转存失败重新上传取消

image.png

6 LRU(Least recently used,最近最少使用)

3.2.1 栗子

经常访问的元素会被追加到队尾,这样不经常访问的数据自然就靠近队头,然后可以通过设置删除策略,比如当 Map 元素个数大于多少时,把头节点删除

3.2.2 元素被移到队尾

get 时,元素会被移动到队尾:

image.png

`public` `V get(Object key) {`
`Node<K,V> e;`
`// 调用 HashMap  get 方法`
`if` `((e = getNode(hash(key), key)) == ``null``)`
`return` `null``;`
`// 如果设置了 LRU 策略`
`if` `(accessOrder)`
`// 这个方法把当前 key 移动到队尾`
`afterNodeAccess(e);`
`return` `e.value;`
`}`

从上述源码中,可以看到,通过 afterNodeAccess 方法把当前访问节点移动到了队尾,其实不仅仅是 get 方法,执行 getOrDefault、compute、computeIfAbsent、computeIfPresent、merge 方法时,也会这么做,通过不断的把经常访问的节点移动到队尾,那么靠近队头的节点,自然就是很少被访问的元素了。


目录
相关文章
|
1天前
|
Linux 网络安全 Windows
网络安全笔记-day8,DHCP部署_dhcp搭建部署,源码解析
网络安全笔记-day8,DHCP部署_dhcp搭建部署,源码解析
|
2天前
HuggingFace Tranformers 源码解析(4)
HuggingFace Tranformers 源码解析
6 0
|
2天前
HuggingFace Tranformers 源码解析(3)
HuggingFace Tranformers 源码解析
6 0
|
2天前
|
开发工具 git
HuggingFace Tranformers 源码解析(2)
HuggingFace Tranformers 源码解析
6 0
|
2天前
|
并行计算
HuggingFace Tranformers 源码解析(1)
HuggingFace Tranformers 源码解析
8 0
|
3天前
PandasTA 源码解析(二十三)
PandasTA 源码解析(二十三)
41 0
|
3天前
PandasTA 源码解析(二十二)(3)
PandasTA 源码解析(二十二)
34 0
|
3天前
PandasTA 源码解析(二十二)(2)
PandasTA 源码解析(二十二)
41 2
|
3天前
PandasTA 源码解析(二十二)(1)
PandasTA 源码解析(二十二)
33 0
|
3天前
PandasTA 源码解析(二十一)(4)
PandasTA 源码解析(二十一)
24 1

推荐镜像

更多