LinkedHashMap的实现原理

1. LinkedHashMap概述:

   LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
   LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。
   注意,此实现不是同步的。如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。

 

2. LinkedHashMap的实现:

   对于LinkedHashMap而言,它继承与HashMap、底层使用哈希表与双向链表来保存所有元素。其基本操作与父类HashMap相似,它通过重写父类相关的方法,来实现自己的链接列表特性。下面我们来分析LinkedHashMap的源代码:

   1) Entry元素:

   LinkedHashMap采用的hash算法和HashMap相同,但是它重新定义了数组中保存的元素Entry,该Entry除了保存当前对象的引用外,还保存了其上一个元素before和下一个元素after的引用,从而在哈希表的基础上又构成了双向链接列表。看源代码:

 1 /** 
 2  * 双向链表的表头元素。 
 3  */  
 4 private transient Entry<K,V> header;  
 5   
 6 /** 
 7  * LinkedHashMap的Entry元素。 
 8  * 继承HashMap的Entry元素,又保存了其上一个元素before和下一个元素after的引用。 
 9  */  
10 private static class Entry<K,V> extends HashMap.Entry<K,V> {  
11     Entry<K,V> before, after;  
12     ……  
13 } 

 2) 初始化:

   通过源代码可以看出,在LinkedHashMap的构造方法中,实际调用了父类HashMap的相关构造方法来构造一个底层存放的table数组。如:

public LinkedHashMap(int initialCapacity, float loadFactor) {  super(initialCapacity, loadFactor);  accessOrder = false;  
} 

HashMap中的相关构造方法:

 1 public HashMap(int initialCapacity, float loadFactor) {  
 2     if (initialCapacity < 0)  
 3         throw new IllegalArgumentException("Illegal initial capacity: " +  
 4                                            initialCapacity);  
 5     if (initialCapacity > MAXIMUM_CAPACITY)  
 6         initialCapacity = MAXIMUM_CAPACITY;  
 7     if (loadFactor <= 0 || Float.isNaN(loadFactor))  
 8         throw new IllegalArgumentException("Illegal load factor: " +  
 9                                            loadFactor);  
10   
11     // Find a power of 2 >= initialCapacity  
12     int capacity = 1;  
13     while (capacity < initialCapacity)  
14         capacity <<= 1;  
15   
16     this.loadFactor = loadFactor;  
17     threshold = (int)(capacity * loadFactor);  
18     table = new Entry[capacity];  
19     init();  
20 }  

我们已经知道LinkedHashMap的Entry元素继承HashMap的Entry,提供了双向链表的功能。在上述HashMap的构造器
中,最后会调用init()方法,进行相关的初始化,这个方法在HashMap的实现中并无意义,只是提供给子类实现相关的初始化调用。
   LinkedHashMap重写了init()方法,在调用父类的构造方法完成构造后,进一步实现了对其元素Entry的初始化操作。

void init() {  header = new Entry<K,V>(-1, null, null, null);  header.before = header.after = header;  
}  

 3) 存储:

   LinkedHashMap并未重写父类HashMap的put方法,而是重写了父类HashMap的put方法调用的子方法void addEntry(int hash, K key, V value, int bucketIndex) 和void createEntry(int hash, K key, V value, int bucketIndex),提供了自己特有的双向链接列表的实现。

 1 void addEntry(int hash, K key, V value, int bucketIndex) {  
 2     // 调用create方法,将新元素以双向链表的的形式加入到映射中。  
 3     createEntry(hash, key, value, bucketIndex);  
 4   
 5     // 删除最近最少使用元素的策略定义  
 6     Entry<K,V> eldest = header.after;  
 7     if (removeEldestEntry(eldest)) {  
 8         removeEntryForKey(eldest.key);  
 9     } else {  
10         if (size >= threshold)  
11             resize(2 * table.length);  
12     }  
13 }  
14 
15 void createEntry(int hash, K key, V value, int bucketIndex) {  
16     HashMap.Entry<K,V> old = table[bucketIndex];  
17     Entry<K,V> e = new Entry<K,V>(hash, key, value, old);  
18     table[bucketIndex] = e;  
19     // 调用元素的addBrefore方法,将元素加入到哈希、双向链接列表。  
20     e.addBefore(header);  
21     size++;  
22 }  
23 
24 private void addBefore(Entry<K,V> existingEntry) {  
25     after  = existingEntry;  
26     before = existingEntry.before;  
27     before.after = this;  
28     after.before = this;  
29 }  

4) 读取:

   LinkedHashMap重写了父类HashMap的get方法,实际在调用父类getEntry()方法取得查找的元素后,再判断当排序模式accessOrder为true时,记录访问顺序,将最新访问的元素添加到双向链表的表头,并从原来的位置删除。由于的链表的增加、删除操作是常量级的,故并不会带来性能的损失。

 1 public V get(Object key) {  
 2     // 调用父类HashMap的getEntry()方法,取得要查找的元素。  
 3     Entry<K,V> e = (Entry<K,V>)getEntry(key);  
 4     if (e == null)  
 5         return null;  
 6     // 记录访问顺序。  
 7     e.recordAccess(this);  
 8     return e.value;  
 9 }  
10 
11 void recordAccess(HashMap<K,V> m) {  
12     LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;  
13     // 如果定义了LinkedHashMap的迭代顺序为访问顺序,  
14     // 则删除以前位置上的元素,并将最新访问的元素添加到链表表头。  
15     if (lm.accessOrder) {  
16         lm.modCount++;  
17         remove();  
18         addBefore(lm.header);  
19     }  
20 }  

  5) 排序模式:

   LinkedHashMap定义了排序模式accessOrder,该属性为boolean型变量,对于访问顺序,为true;对于插入顺序,则为false。

private final boolean accessOrder;  

 一般情况下,不必指定排序模式,其迭代顺序即为默认为插入顺序。看LinkedHashMap的构造方法,如:

public LinkedHashMap(int initialCapacity, float loadFactor) {  super(initialCapacity, loadFactor);  accessOrder = false;  
} 

  这些构造方法都会默认指定排序模式为插入顺序。如果你想构造一个LinkedHashMap,并打算按从近期访问最少到近期访问最多的顺序(即访问顺序)来保存元素,那么请使用下面的构造方法构造LinkedHashMap:

public LinkedHashMap(int initialCapacity,  float loadFactor,  boolean accessOrder) {  super(initialCapacity, loadFactor);  this.accessOrder = accessOrder;  
}  

该哈希映射的迭代顺序就是最后访问其条目的顺序,这种映射很适合构建LRU缓存。LinkedHashMap提供了removeEldestEntry(Map.Entry<K,V> eldest)方法,在将新条目插入到映射后,put和 putAll将调用此方法。该方法可以提供在每次添加新条目时移除最旧条目的实现程序,默认返回false,这样,此映射的行为将类似于正常映射,即永远不能移除最旧的元素。

protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {  return false;  
} 

此方法通常不以任何方式修改映射,相反允许映射在其返回值的指引下进行自我修改。如果用此映射构建LRU缓存,则非常方便,它允许映射通过删除旧条目来减少内存损耗。
   例如:重写此方法,维持此映射只保存100个条目的稳定状态,在每次添加新条目时删除最旧的条目。

private static final int MAX_ENTRIES = 100;  
protected boolean removeEldestEntry(Map.Entry eldest) {  return size() > MAX_ENTRIES;  
} 

 

转载于:https://www.cnblogs.com/ganchuanpu/p/8908093.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283485.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

验证-表单令牌

表单令牌验证规则支持对表单的令牌验证&#xff0c;首先需要在你的表单里面增加下面隐藏域&#xff1a;或者然后在你的验证规则中&#xff0c;添加token验证规则即可&#xff0c;例如&#xff0c;如果使用的是验证器的话&#xff0c;可以改为&#xff1a;如果你的令牌名称不是_…

WPF 用户控件分享之边上带输入框的圆圈

WPF 用户控件分享之边上带输入框的圆圈独立观察员 2022 年 8 月 20 日最近有这样一个需求&#xff0c;有一圈圆形&#xff0c;每个圆形边上有个输入框&#xff0c;以下是完成后的效果图&#xff1a;拿到这个需求后&#xff0c;分析界面上每个圆形和输入框应该视为一个用户控件&…

总统与乞丐

一位总统带着孙子散步&#xff0c;有个乞丐向他鞠躬敬礼&#xff0c;总统马上驻足还礼&#xff0c;而且弯腰更深。孙子不解:“他只是个乞丐啊&#xff01;” 总统回答&#xff1a;“我绝不允许一个乞丐比总统更有礼貌&#xff01;不要以为别人尊敬你&#xff0c;是因为你很优秀…

VUE3 v-show 引起的表单验证问题

<el-form ref"formRef" :rules"rules" :model"form" label-width"80px"><el-form-item label"任务类型" prop"TaskType"><el-radio-group v-model"form.TaskType" change"handleCh…

ThreadPoolExecutor

简述&#xff1a; 有的时候&#xff0c;系统处理很多任务&#xff0c;如何这些任务要是都是通过new Thread来做的话&#xff0c;系统就不得不常常的创建之后还要销毁Thread&#xff0c;这个是非常消耗时间而且还占用资源&#xff0c;所以我们通过创建线程池来管理我们的线程。 …

你总说时间很少

你总说时间很少 没时间看哭泣的骆驼 少了心情 你总说时间很少 没时间撩拨那爱的罗曼史 少了喜欢 你总说时间很少 没时间吮吸印度洋的水清沙白 少了勇敢 你总说时间很少 没时间寻找相伴的野马 少了感觉 可到最后 时间真的很少 有了喜欢 就少了心情 有了感觉 就少了勇敢…

Unity 将是驱动 C# 增长的引擎吗 ?

C# 在中国的采用需要一个杀手级应用的带动&#xff0c; 那么这样的一个杀手级应用是 Unity吗&#xff0c;我这里大胆推测采用CoreCLR 的新一代完全采用C#构建的Unity 将是这样的一个杀手级应用。Unity已被广泛应用于数字孪生、数字城市、数字工厂等场景&#xff0c;成为各产业加…

HTML5video 标签

属性 值 说明 autoplay autoplay 如果出现该属性&#xff0c;则视频在就绪后马上播放。 controls controls 如果出现该属性&#xff0c;则向用户显示控件&#xff0c;比如播放按钮。 preload preload 如果出现该属性&#xff0c;则视频…

Windows 超级开源实用工具:Microsoft PowerToys

实用工具集&#xff1a; Always on Top ​通过 Always on Top&#xff0c;可使用快捷键方式 (⊞ WinCtrlT) 将窗口固定在其他窗口的顶部。 ​ PowerToys Awake PowerToys Awake旨在使计算机保持唤醒状态&#xff0c;且无需管理其电源和睡眠设置。 运行耗时较长的任务时&#…

Python入门之数据类型

字符串 列表 元组 字典 转载于:https://www.cnblogs.com/py17/p/8919495.html

Kubeadm 快速搭建 k8s v1.24.1 集群(openEuler 22.03 LTS)

kubeadm 简介kubeadm 是 Kubernetes&#xff08;以下简称 k8s&#xff09;官方提供的用于快速安装部署 k8s 集群的工具&#xff0c;伴随 k8s 每个版本的发布都会同步更新&#xff0c;kubeadm 会对集群配置方面的一些实践做调整&#xff0c;通过实验 kubeadm 可以学习到 k8s 官方…

汇编试验五:编写、调试具有多个段的程序

ds 数据段放入数据正确&#xff1b; 两次push 操作后&#xff0c;ss栈段正确&#xff1b; 由于pop 操作顺序&#xff0c;ds数据段并没有发生改变&#xff1b; Source Code: assume cs:code, ds:data, ss:stackdata segmentdw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H …

SVN四部曲之SVN设置详解深入

想知道不同的设置是干什么用的&#xff0c;你只需将鼠标指针在编辑框/选项框上停留一秒钟...一个帮助提示气泡就会弹出来。 常规设置 图 4.68. 设置对话框&#xff0c;常规设置页面 这个对话框允许你指定自己喜欢的语言&#xff0c;同时也可做那些与Subversion相关的特殊设置。…

Vue3.2单文件组件setup的语法糖总结

目录 前言 setup语法糖 一、基本用法 二、data和methods 三、计算属性computed 四、监听器watch、watchEffect 五、自定义指令directive 六、import导入的内容可直接使用 七、声明props和emits 八、父组件获取子组件的数据 九、provide和inject传值 十、路由useRou…

自动判断PC端、手机端跳往不同的域名JS实现代码

输入相同域名&#xff0c;在pc端和移动端会出现不同的页面效果&#xff0c;一种是用栅格系统实现自适应&#xff0c; 更多的是设计两套不同的模板和两个二级域名或者一个主域名和一个二级域名&#xff08;就是有区别就可以了&#xff09;; js代码判断浏览器的用户代理头类别从而…

使用dotnet-monitor分析在Kubernetes的应用程序:Sidecar模式

dotnet-monitor可以在Kubernetes中作为Sidecar运行&#xff0c;Sidecar是一个容器&#xff0c;它与应用程序在同一个Pod中运行&#xff0c;利用Sidecar模式使我们可以诊断及监控应用程序。如下图所示&#xff0c;这是我们最终要实现的目标&#xff0c;通过可视化界面查看应用程…

C/C++语言的特点

一、支持数据封装和数据隐藏  在C中&#xff0c;类是支持数据封装的工具&#xff0c;对象则是数据封装的实现。C通过建立用户定义类支持数据封装和数据隐藏。  在面向对象的程序设计中&#xff0c;将数据和对该数据进行合法操作的函数封装在一起作为一个类的定义。对象被说…

JS中的扩展运算符(...)和剩余运算符(...)

文章目录 一、概念二、扩展运算符三、剩余运算符四、总结五、参考资料一、概念 在JS中&#xff0c;扩展运算符&#xff08;spread&#xff09;是三个点 (...) &#xff0c;剩余运算符&#xff08;rest&#xff09;也是三个点 (...) 二、扩展运算符 &#xff08;1&#xff09;基…

SFB 项目经验-07-Skype for Business 话机 Polycom CX700

本系列博文&#xff1a;Lync 项目经验-01-共存迁移-Lync2013-TO-SFB 2015-规划01http://dynamic.blog.51cto.com/711418/1858520 Lync 项目经验-02-共存迁移-Lync2013-TO-SFB 2015-规划02http://dynamic.blog.51cto.com/711418/1859143 Lync 项目经验-03-共存迁移-Lync2013-TO…

亿方云CEO程远:转型第一式:链接企业人与数据

传统企业一直在探讨如何做好互联网转型&#xff0c;那么互联网核心是什么&#xff1f;转型目标是什么&#xff1f;亿方云CEO程远在此次峰会上发表了自己看法&#xff0c;他认为快、人、连接是互联网核心基因&#xff0c;转型第一步就在于企业、人与数据的充分链接。中国企业的互…