文章目录

  • LinkedHashMap 简介
  • 一、实现原理
  • 二、源码分析
  • 2.1 继承与实现关系
  • 2.2 重要成员信息
  • 2.3 构造方法
  • 2.4 数据结构
  • 2.5 重要方法
  • 2.5.1 存储put
  • 2.5.2 读取get
  • 2.5.3 移除remove


LinkedHashMap 简介

LinkedHashMap是HashMap的子类,每个键值对即位于哈希表中,也位于双向链表中。可以认为它是一个带链表的HashMap,组合了双向链表和哈希表,内部使用了双向链表来维护key-value键值对的次序(其实只考虑key的次序),该链表保证了元素的迭代顺序,默认迭代顺序与key-value键值对的插入顺序保持一致。

LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能。由于LinkedHashMap继承了HashMap,所以其具有了HashMap的一切性质。

opentelemetry展示java调用链路拓扑图 java linkmap_链表

一、实现原理

LinkedHashMap,它继承了HashMap,底层使用哈希表和双向链表来保存所有元素。其基本操作和HashMap基本相似,它通过重写父类的方法,来实现自己的链接列表的特性。

二、源码分析

2.1 继承与实现关系

opentelemetry展示java调用链路拓扑图 java linkmap_父类_02


opentelemetry展示java调用链路拓扑图 java linkmap_双向链表_03

  1. LinkedHashMap 继承了HashMap,所以它的底层仍然是基于链式散列结构。
  2. LinkedHashMap 实现了 Map 接口,所以它是一个Map,即一个key-value集合。它是哈希表和链接列表实现,具有可预知的迭代顺序。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
  3. LinkedHashMap,它默认保留插入的顺序。
  4. LinkedHashMap 实现与 HashMap 的不同之处在于,LinkedHashMap 维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。
  5. LinkedHashMap其基本操作与父类 HashMap 相似,它通过重写父类相关的方法,来实现自己的链接列表特性。比如put、remove方法就是直接用的父类的(仅为维护双向链表覆写了部分方法),get方法是重写的。

2.2 重要成员信息

除了从HashMap继承过来的属性外,新增了三个属性用于保证迭代顺序:

opentelemetry展示java调用链路拓扑图 java linkmap_双向链表_04

  1. head:双向链表的头(最老)(前置指针)
  2. tail:双链表的末尾(最小)(后置指针)
  3. accessOrder:链表顺序,false是默认插入顺序,true是遍历访问顺序(把最后访问的节点放到双向链表的最后一位, 访问的方式有替换旧节点和读取节点)

2.3 构造方法

opentelemetry展示java调用链路拓扑图 java linkmap_双向链表_05


从其构造方法中可以看出来,默认都是采用插入顺序来维持取出键值对的顺序。所有构造方法都是通过调用父类HashMap的构造方法来创建对象的。

2.4 数据结构

  • 数据结构
    LinkedHashMap采用的Hash算法和HashMap相同,但又重新定义了 Entry,并让Entry 继承了HashMap的 Node。Entry中还添加了指向上一个元素的 before和指向下一个元素的 after,这样就在哈希表的基础上又构成了双向链接列表。

    HashMap 底层存储键值对的数据结构是 Node 和 TreeNode,而 LinkedHashMap 存储键值对的数据结构是 Entry 和 TreeNode。
    HashMap中的TreeNode 类继承了 LinkedHashMap 的 Entry,这样一来就继承了父类的前置与后置指针,也就能维护 LinkedHashMap 的插入顺序了。

2.5 重要方法

2.5.1 存储put

LinkedHashMap存储值,其实是调用其父类HashMap的put方法进行存储值,其维持双链结构以及保持有序,在于以下几点:

  1. 重写了newNode(int hash, K key, V value, Node<K,V> e)方法,控制新增节点追加到链表的尾部,这样每次新节点都追加到尾部,即可保证插入顺序了。
    链表节点:

    树节点:

    其中linkNodeLast方法将新构造的双向链表节点插入到链的尾部:
  2. 重写了afterNodeAccess方法
    这个方法在HashMap中是空实现,是在发生hash冲突后,找到相同key对值进行处理时用。该方法在LinkedHashMap中被重写,此方法可以实现通过访问顺序排序,方法中如果定义accessOrder=true,则会将访问过的元素放到链表尾部【accessOrder设置可以通过构造器方法传递】。
  3. 重写了afterNodeInsertion方法
    这个方法在新节点插入才会触发,HashMap中evite一直为true,而removeEldestEntry始终返回false,所以此方法无实际意义。但若重写了removeEldestEntry方法,则可以实现LRU(近少使用)缓存
2.5.2 读取get

LinkedHashMap重写了get方法,如下:

opentelemetry展示java调用链路拓扑图 java linkmap_父类_06


在我们指定accessOrder=true情况下,即让其按照访问顺序维护链表。其查找key对应的vlaue方法与HashMap一样。

2.5.3 移除remove

LinkedHashMap的删除节点与HashMap基本一致,唯一区别在于HashMap中的afterNodeRemoval为空实现,而LinkedHashMap重写了该方法,用于删除节点后维护双向链表。

opentelemetry展示java调用链路拓扑图 java linkmap_父类_07