LinkedHashMap与HashMap的区别

LinkedHashMap与HashMap的区别这又是一道面试题 所以这里先给一些结论 然后分析代码 1 总结 LinkedHashMa 继承 HashMap 所以拥有绝大部分 HashMap 的特性 更多细节见 https blog csdn net newchenxf article details 这包括 存储用数组 数组大小动态扩大 线程不安全 key 可以为 null 等等 1 1 关键区别 HashMap 因为 index 是随机生成的 所以每次 put 存放的位置是无序的 虽然节点类有 next 但这个单链表仅是在同一个 ind

码字不易,转载请注明出处喔:https://blog.csdn.net/newchenxf/article/details/


这又是一道面试题。所以这里先给一些结论,然后分析代码。

1. 总结

LinkedHashMap继承HashMap,所以拥有绝大部分HashMap的特性(更多细节见:https://blog.csdn.net/newchenxf/article/details/)。

这包括,存储用数组,数组大小动态扩大。线程不安全,key可以为null等等。

1.1 关键区别

HashMap因为index是随机生成的,所以每次put,存放的位置是无序的。(虽然节点类有next,但这个单链表仅是在同一个index的坑位,是串联的^^)

LinkedHashMap通过额外维护一个双向链表,来保证迭代顺序。所以它是有序的。即,它是有办法知道谁先入,谁后入的。当然,为此也增加了时间/空间的开销。

2. 细化分析

2.1 额外增加的双向链表定义

 / * LinkedEntry adds nxt/prv double-links to plain HashMapEntry. */ static class LinkedEntry<K, V> extends HashMapEntry<K, V> { 
     LinkedEntry<K, V> nxt; LinkedEntry<K, V> prv; / Create the header entry */ LinkedEntry() { 
     super(null, null, 0, null); nxt = prv = this; } / Create a normal entry */ LinkedEntry(K key, V value, int hash, HashMapEntry<K, V> next, LinkedEntry<K, V> nxt, LinkedEntry<K, V> prv) { 
     super(key, value, hash, next); this.nxt = nxt; this.prv = prv; } } 

就是继承HashMap的内部类HashMapEntry,然后新增了2个指针nxt & prv,指向前后节点,所以,它变成了一个双向链表的节点类。

2.2 额外增加的成员变量

相比HashMap,额外加了2个成员变量。分别是 双向链表 头节点header (在LinkedHashMap构造函数初始化)和 标志位accessOrder (值为true时,表示按照访问顺序迭代;值为false时,表示按照插入顺序迭代)。

定义如下:

 // 双向链表的表头元素 transient LinkedEntry<K, V> header; //true表示按照访问顺序迭代,false时表示按照插入顺序, 默认false private final boolean accessOrder; 

2.3 数据插入

LinkedHashMap倒是重写了addNewEntry。我们来看一下这个函数的实现。

 @Override void addNewEntry(K key, V value, int hash, int index) { 
     LinkedEntry<K, V> header = this.header; //....省略部分不重要代码 // Create new entry, link it on to list, and put it into table LinkedEntry<K, V> oldTail = header.prv; LinkedEntry<K, V> newTail = new LinkedEntry<K,V>( key, value, hash, table[index], header, oldTail); table[index] = oldTail.nxt = header.prv = newTail; } 

很简单,就是一些指针的方向转换。我画个图就明白了。

第一次添加元素:
在这里插入图片描述
这时候,唯一的元素E1和head互相连着。你能找到我,我也能找到你,如胶似漆。




第二次添加元素:
在这里插入图片描述

这时候,形成了三角恋,E1连着新同学E2, E2连着head,但E1也同时连着head。剪不断,理还乱。

第三次添加元素:
在这里插入图片描述

这时候,是四角恋了,关系更复杂。

最初的那个男人head,和最新来的同学E3,以及最早来的同学E1,都保持着联系。都不知道要说他专情,还是喜新厌旧哩^^

2.4 读取元素

 @Override public V get(Object key) { 
     //省略部分代码 int hash = Collections.secondaryHash(key); HashMapEntry<K, V>[] tab = table; //和HashMap一样,根据hash值生成Index,即(hash & (tab.length - 1)),然后读取节点。 for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { 
     K eKey = e.key; if (eKey == key || (e.hash == hash && key.equals(eKey))) { 
     //如果accessOrder为true,则被读取的节点,要变成链表的尾巴 if (accessOrder) makeTail((LinkedEntry<K, V>) e); return e.value; } } return null; } 

从代码可知,读取的方式,和HashMap是一样的。无非是中间加个额外的事情,是,如果accessOrder为true,则被读取的节点,要变成链表的尾巴(makeTail)。

makeTail是如何实现呢?

 private void makeTail(LinkedEntry<K, V> e) { 
     // Unlink e e.prv.nxt = e.nxt; e.nxt.prv = e.prv; // Relink e as tail LinkedEntry<K, V> header = this.header; LinkedEntry<K, V> oldTail = header.prv; e.nxt = header; e.prv = oldTail; oldTail.nxt = header.prv = e; modCount++; } 

简单的很,就是把header.prv改成当前节点。

通常认为,header的nxt是第一个节点,header的prv是最后一个节点。

2.5 链表的作用

看完插入数据和读取数据,发现双线链表,仅仅是生成,并么有什么用途?那可以用到哪里呢?

它用途确实不明显!倒是有一个需求,是要判断哪个entry是最早的来的。比如上面画图的E1。

 public Entry<K, V> eldest() { 
     LinkedEntry<K, V> eldest = header.nxt; return eldest != header ? eldest : null; } 

2.6 再次小结

所以说,一般HashMap已经足够了,除非你有强需求,需要知道数据顺序,想要了解谁最早插入,才用开销大一些的LinkedHashMap!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/209360.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 上午9:25
下一篇 2026年3月19日 上午9:26


相关推荐

  • 51单片机控制步进电机-电路连接[通俗易懂]

    51单片机控制步进电机-电路连接[通俗易懂]51单片机控制步进电机-电路连接概要:本案例讲解的内容是51单片机控制步进电机硬件连接部分。后续会分别讲解单片机程序,S曲线加减速方法,上位机等相关内容硬件清单:1、51单片机控制板一个2、二相四线步进电机一个3、稳压电源一个4、TB6600步进电机驱动器一个整体连接图:原理图:功能部分说明:1、51单片机:①输出脉冲到TB6600驱动器PUL端口,从而控制步进电机转动②控制TB6600驱动器ENA端口,从而控制步进电机使能③控制TB6600驱动器DIR端口,从而控制步进电机

    2022年5月31日
    37
  • 谷歌搜索语法大全_Google语法

    谷歌搜索语法大全_Google语法Google是一款十分强大的搜索引擎,黑客们常常借助它搜索网站的一些敏感目录和文件,甚至可以利用它的搜索功能来自动攻击那些有漏洞的网站;而有些人可以通过搜索把某个个人的信息,包括住址、电话号码、出生年月等都可以搜索出来;当然我们在日常的生活中正确的借助Google搜索也可以更加高效的找到我们需要的东西。

    2025年10月25日
    5
  • DrawerLayout详解「建议收藏」

    DrawerLayout详解「建议收藏」drawerLayout是SupportLibrary包中实现了侧滑菜单效果的控件,可以说drawerLayout是因为第三方控件如MenuDrawer等的出现之后,google借鉴而出现的产物。drawerLayout分为侧边菜单和主内容区两部分,侧边菜单可以根据手势展开与隐藏(drawerLayout自身特性),主内容区的内容可以随着菜单的点击而变化(这需要使用者自己实现)。

    2022年6月16日
    33
  • Hadoop生态圈hive应用

    Hadoop生态圈hive应用第1章Hive基本概念1.1什么是HiveHive:由Facebook开源用于解决海量结构化日志的数据统计。Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类SQL查询功能。1.2Hive的优缺点1.2.1优点1)操作接口采用类SQL语法,提供快速开发的能力(简单、容易上手)。2)避免了去写MapReduce,减少开发人员的学习成本。3)Hive的执行延迟比较高,因此Hive常用于数

    2022年5月11日
    49
  • origin 2018安装教程与安装包

    origin 2018安装教程与安装包百度网盘地址:链接:https://pan.baidu.com/s/11Lo7R4YvldtkcKZxb5vbXQ提取码:aq8o这里是安装包大概1.8G,比较大。如果链接失效可以联系我913121976@qq.com邮箱。有什么问题可以一起交流!1、打开安装文件夹里的setup,进行安装。点击下一步2、同意许可,下一步。点击下一步3、客户信息,前两个可以随便填写,第三个…

    2022年4月27日
    138
  • 挖矿程序处理[通俗易懂]

    挖矿程序处理[通俗易懂]记一次工作中遇到得挖矿程序处理首先需要减少中毒得几率,就是不要把ssh密码设得太简单,然后ssl端口号改改,改加的访问次数限制加上,常用的sql,代码管理工具等等port也都改掉,管理员权限账户不要多建挖矿程序特点,cpu占用率贼高300,kill不尽,会出现一些自己不曾安装过的程序,库等挖矿程序一般是杀死不净的,需要找到程序路径,以及自启动的脚本ls/proc/进程号/exe-la删掉相关程序but你会发现,它在其他地方又新建了脚本…

    2022年6月29日
    31

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号