[Java] LinkedHashMap 源码简要分析

本文深入分析Java LinkedHashMap的源码,揭示其如何通过双向链表实现键值对的顺序存储,并阐述put()、get()等关键方法的实现细节,特别强调了get()命中后元素被放置到链表末尾的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[Java] LinkedHashMap 源码简要分析

 

 特点

* 各个元素不仅仅按照HashMap的结构存储,而且每个元素包含了before/after指针,通过一个头元素header,形成一个双向循环链表。使用循环链表,保存了元素插入的顺序。
* 可设置参数,让每次get()后的元素排在双向链表的最后。
 
Entry类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private  static  class  Entry<K,V>  extends  HashMap.Entry<K,V>  // 继承自HashMap的Entry(已有key/value/hash/next字段)
{
      // 双向链表
      Entry<K,V> before;
      Entry<K,V> after;
 
      // 构造函数
      Entry( int  hash, K key, V value, HashMap.Entry<K,V> next) {
             super (hash, key, value, next);
      }
      
      // 删除当前结点
      private  void  remove(){
           before.after = after;
           after.before = before;
      }
 
      // 在当前结点的前面添加结点
      private  void  addBefore(Entry<K,V> existingEntry){
             after  = existingEntry;
             before = existingEntry.before;
             before.after =  this ;
             after.before =  this ;         
      }
 
      // 如果设定某个参数,get()命中后,把当前元素放到链表最后
      void  recoreAccess(HashMap<K,V> m) {
           LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;      // 把调用者的this转化成LinkedHashMap
           if (lm.accessOrder){
                remove();  // 删除当前结点
                addBefore(lm.header);  // 插入到header前面
           }
      }
}

  

源码简要分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public  class  LinkedHashMap<K,V>  extends  HashMap<K,V>
{
      private  Entry<K,V> header;  // 双向链表的头部。
      private  final  boolean  accessOrder;  // 默认false。如果true表示get()命中后,把当前元素放到链表最后。
 
      // init()
     void  init() {
         header =  new  Entry<>(- 1 null null null );
         header.before = header.after = header;  // 双向链表首尾相连
     }
 
      // put() 继承自 HashMap
      // 添加元素时,不仅按照HashMap的方式散列存储,而且还通过双向链表记录先后顺序
      public  V put(K key, V value) {
           int  hash = hash(key.hashCode());      // key的特殊hash值
           int  i = indexFor(hash,table.length);      // 槽位 index
 
           // key是否已经存在,存在则返回value。
           for  (Entry<K,V> e = table[i]; e !=  null ; e = e.next) {
             Object k;
             if  (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                 V oldValue = e.value;
                 e.value = value;
                 e.recordAccess( this );  //LinkedHashMap特有
                 return  oldValue;
             }
               }
 
                // key不存在就添加Entry
                addEntry(hash,key,value,i);
                return  null ;
      }
 
     void  addEntry( int  hash, K key, V value,  int  bucketIndex) {
         createEntry(hash, key, value, bucketIndex);
 
         // Remove eldest entry if instructed, else grow capacity if appropriate
         Entry<K,V> eldest = header.after;
         if  (removeEldestEntry(eldest)) {
             removeEntryForKey(eldest.key);
         else  {
             if  (size >= threshold)
                 resize( 2  * table.length);
         }
     }
 
     void  createEntry( int  hash, K key, V value,  int  bucketIndex /*槽位index*/ ) {
         HashMap.Entry<K,V> old = table[bucketIndex];
         Entry<K,V> e =  new  Entry<>(hash, key, value, old);
         table[bucketIndex] = e;
         e.addBefore(header);
         size++;
     }
 
      // get()
      // 取元素是按照散列而不是双向链表进行查找,速度快
     public  V get(Object key) {
         Entry<K,V> e = (Entry<K,V>)getEntry(key);  // getEntry()使用了HashMap的getEntry,即按照HashMap的方式寻找元素。
         if  (e ==  null )
             return  null ;
         e.recordAccess( this );
         return  e.value;
     }
 
}

  

添加元素的过程示意图

初始化时,头元素header的before/after均指向自身。

 

插入元素e1后,header的before/after均指向e1;e1的before/after均指向header。

 

插入元素e2后,header的after继续指向e1,e1的after指向e2,e1的before指向header。header的before指向e2。e2的before指向e1,e2的after指向header。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值