LinkedHashMap与HashMap的区别

2023-10-31

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


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

1. 总结

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

这包括,存储用数组,数组大小动态扩大。线程不安全,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 数据插入

数据插入用put函数,这个直接用了HashMap的实现,没有override。
put函数基本是做三件事,一是根据key的hashcode,生成数组的index;二是看数据量,看要不要扩容;三是添加值,即addNewEntry。

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!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LinkedHashMap与HashMap的区别 的相关文章

  • C++派生类的不同继承方式对基类的访问权限

    经过我细心的整理 形成了这张表 一张表说明派生类的不同继承方式 对基类的访问权限 总的来说 对类的访问权限范围public
  • 2022 CISCN初赛 Satool

    一个2022年国赛初赛的LLVM PASS类pwn题 当时还完全没有接触过 所以直接放弃掉了 初赛结束之后决定入门一下这方面知识 看这篇题解之前最好先看看之前写的这篇入门文章 LLVM PASS类pwn题入门 然后我们正式开始这道题 首先从
  • 07-js 逆向-返回数据加密(aes)

    目标 返回的结果有加密 把结果解密 可以看到返回来的data是加密的 但是加密的数据并没有进行混淆 这时候我们可以采用直接搜解密 decrypt 直接发先我们的数据书通过aes加密的 我们开始些python代码 from Crypto Ci
  • vndk: (native:vendor) should not link to libcamera_client (native:platform)

    1 0 相似例子 2 21 17 47 30 305 4365 4365 E CamX ERROR UTILS camxosutilslinux cpp 874 LibMap dlopen dlopen failed library lib
  • 利用mimikatz查看rdp连接密码【渗透测试】

    0x00 概述 在使用 rdp 时会发现系统有保存连接密码的功能 一定在本地以一种加密方式保存 在连接的时候解密进行rdp尝试 那么我们能不能那到加密的密码解密以获取这台机器rdp连接过的机器呢 0x01 流程 AppData Local
  • PUMA:DOA估计模式的改进实现(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 Matlab代码实现 4 参考文献 1 概述 文献来源 下载链接 PUMA An Imp
  • ue4添加第三方库

    查了一些资料 发现最后都是用loadlibrary的方式 这样很不方便 如果有10000个函数 要写10000次么 仔细想想 调用第三方库无非就是把头文件和lib库设置下 把相应的 h lib和 dll放到相应的位置 再在调用的地方包含头文
  • cadence原理图封装pin名称重复_Cadence原理图库文件引脚名重复处理方法介绍

    立题简介 内容 Cadence原理图库文件引脚名重复处理方法 来源 实际使用得出 作用 介绍2种处理Cadence原理图库文件引脚名的方法 PCB环境 Cadence 16 6 orCAD环境 日期 2019 03 09 分割线 立题详解
  • spring打印http接口请求和响应

    在程序日志中打印出接口请求和响应的内容是一个基本的技术需求 如果在每个接口中实现请求响应的日志打印 程序编写会很繁琐 我们可以利用spring提供的机制 集中处理接口请求响应的日志打印 具体的代码参照 示例项目 https github c

随机推荐

  • 使用ipmitool命令检测电源模块状态

    1 通过ipmitool检查电源模块状态 https mp weixin qq com s Z1g79Q1aMhOT9Xm9fvIkjg 2 通过ipmitool获取服务器各元件温度信息 https mp weixin qq com s E
  • 大数据分布式计算开源框架Hadoop的介绍和运用

    Hadoop是Apache开源组织的一个分布式计算开源框架 在很多大型网站上都已经得到了应用 如亚马逊 Facebook和Yahoo等等 对于我来说 最近的一个使用点就是服务集成平台的日志分析 服务集成平台的日志量将会很大 而这也正好符合了
  • vue 快速自定义分页el-pagination

    vue 快速自定义分页el pagination template div style text align center div
  • main函数中的参数代表的意义

    int main int argc char argv 或者是 int main int argc char argv 里面的参数是什么意义呢 argc 是 argument count的缩写 表示传入main函数的参数个数 argv 是
  • 分享一个完整的Mybatis分页解决方案

    原文地址 http duanhengbin iteye com blog 1998017 参考地址 http blog csdn net isea533 article details 23831273 Mybatis 的物理分页是应用中的
  • 一起学ORBSLAM2(11)ORBSLAM的localmapping

    转载请注明原创地址 https blog csdn net qq 30356613 article category 6897125 ORBSLAM的局部建图线程实际做的工作是来维护全局map以及管理关键帧的 对tracking得到的关键帧
  • HWND转成CWnd

    在Dialog调用中调用系统的Create函数时 遇到了这个问题 BOOL CWnd Create LPCTSTR lpszClassName LPCTSTR lpszWindowName DWORD dwStyle const RECT
  • java设计模式--[结构模式]--装饰者模式[decorator pattern]

    一 裝飾者模式 裝飾者模式 又叫包裝器 動態地給動象添加一些額外的職責 若要擴展功能 裝飾者指供了比繼承更有彈性的替代方案 二 裝飾者模式的UML類圖如下 三 本節內容以一個點餐配菜的案例來說明裝飾者模式的用法 完整代碼如下 1 主食類超類
  • Qt-sqlite3数据库编程实例

    Qt sqlite3数据库编程实例 版本说明 版本 作者 日期 备注 0 1 loon 2018 10 26 初稿 目录 文章目录 Qt sqlite3数据库编程实例 版本说明 目录 一 需求和目的 二 程序设计 三 源码展示 四 结果展示
  • go 性能相关总结

    链客 专为开发者而生 有问必答 此文章来自区块链技术社区 未经允许拒绝转载 性能测试基本概念 基本概念 Benchmark 性能测试 ns op 纳秒 每个操作 前面数值越小越快 命令 go test c go test test benc
  • python实现大疆Tello无人机控制平台并实现语音控制/手势控制/人脸跟踪/绿球跟踪/拍照录像

    Tello智能信息处理平台 介绍 控制 键盘控制 语音控制 视觉功能 人脸跟踪 绿球跟踪 手势控制 体态控制 拍照录像 结语 介绍 本项目是我的一个课程设计 本来打算做大型四旋翼无人机的控制 后来老师给了两个Tello无人机 分别是带拓展板
  • vue 项目中 sass 如何复用?

    前言 vue项目中 我们在使用scss编写代码的时候 有些时候可能会有很多样式代码是重复的 这个时候我们可以把公共的部分提取出来 文件结构如下图 步骤1 在assets目录下新建css目录 存放css资源 在css目录下新建styles s
  • css选中子元素中不是第一个元素的3种方法

    第一种 使用伪类选择器 not
  • 写代码时发现……还是Python牛逼

    都说Python通俗易懂 容易上手 甚至不少网友表示 完成同一个任务 C 语言要写 1000 行代码 Java 只需要写 100 行 而 Python 可能只要 20 行 到底是真的还是假的 下面就以一个最简单的入门级 Hello Worl
  • nginx配置多个静态html页面

    第一个项目 server listen 80 server name localhost location root C Users Administrator Documents HBuilderProjects billing 根目录
  • SeekBar设置最小值

    seekbar如何设置最小值 项目需求 要让seekbar的最小值为60 大家都知道如何设置初始值 最大值 但是最小值 我在网上查了一些资料 并不需要网上说的那么复杂 例如我的需求是seekbar的范围是60 85 最小值要为60 andr
  • 基于用户的协同过滤算法的电影推荐系统

    上一篇讲解了推荐算法的分类 这里电影推荐系统具体分析一下 第一步 建立用户电影矩阵模型 如表1所示 协同过滤算法的输入数据通常表示为一个m n的用户评价矩阵Matrix m是用户数 n是电影数 Matrix ij 表示第i个用户对第j个电影
  • cocos2d-x 3.x 事件相关源码接口

    绝对干货 欢迎留下足迹 事件类及其子类 事件侦听及其子类 事件注册 分发
  • 基于卷积神经网络的人脸表情识别(JAFFE篇)

    一 前言 人脸表情识别依然是计算机视觉中的研究重点 那就意味着它还有水文章的可能性 本专题将专门讨论各种表情识别的研究方法 当然我们从最简单的单一卷积网络开始 本文中的代码直接复制到电脑端 python 或者服务器 ipython 上都可以
  • LinkedHashMap与HashMap的区别

    码字不易 转载请注明出处喔 https blog csdn net newchenxf article details 118607174 这又是一道面试题 所以这里先给一些结论 然后分析代码 1 总结 LinkedHashMap继承Has