LinkedHashMap常用方法源码

2023-11-07

类介绍(注释)

  1. add、contains、remove 方法,时间复杂度是O(1)。
  2. LinkedHashMap的遍历耗时,与_capacity无关,与map的size(元素多少)呈线性。_
  3. HashMap的遍历,可能比_LinkedHashMap更耗时,其和_capacity呈线性关系。
  4. LinkedHashMap是非线程安全的,并发出错时,会快速失败,抛出ConcurrentModificationException。可以使用_Collections.synchronizedMap(new LinkedHashMap(…));_
  5. LinkedHashMap非常适合于构建LRU缓存least-recently Used)。
  6. 并不是所有的adds、delete操作都会造成LinkedHashMap结构的变更。

_insertion-ordered 模式下,_修改一个已经存在的key,对应的值,并不会造成结构的变更
access-ordered 模式下,get将造成结构的变更

LinkedHashMap的一些概念


// 头结点,同时也是最早插入的节点。
transient LinkedHashMap.Entry<K,V> head;

// 尾结点,同时也是最后插入的节点。
transient LinkedHashMap.Entry<K,V> tail;


// 继承 Node,为数组的每个元素增加了 before 和 after 属性。
// LinkedHashMap 的数据结构很像是把 LinkedList 的每个元素换成了 HashMap 的 Node,像是两者的结合体.
// 也正是因为增加了这些结构,从而能把 Map 的元素都串联起来,形成一个链表,而链表就可以保证顺序了,就可以维护元素插入进来的顺序。
static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

// 控制两种访问模式的字段,默认 false
// true 按照访问顺序,会把经常访问的 key 放到队尾
// false 按照插入顺序提供访问
final boolean accessOrder;

常用方法分析

LinkedHashMap() (无参构造方法)

LinkedHashMap 在无参构造方法中,将accessOrder设置为false,即按照插入顺序访问。它的_capacity、与HashMap的默认的一致,为16、0.75。

public LinkedHashMap() {
    super();
    accessOrder = false;
}

LinkedHashMap按顺序插入节点(put)

首先,put是直接调用HashMap#put方法。从LinkedHashMap 的重要概念中,可以看到,存在一个继承了HashMap.Node的Entry。
当前put时,即会创建LinkedHashMap#Entry对象。并且,LinkedHashMap中重写了HashMap#put中调用的newNodeafterNodeInsertionafterNodeAccess方法。
put的具体流程,可以参照HashMap源码。这里来看LinkedHashMap重写的newNode方法。

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
    // 创建新节点
    LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e);
    // 将节点加到链表尾部
    linkNodeLast(p);
    return p;
}

// link at the end of list
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
    // 备份尾节点
    LinkedHashMap.Entry<K,V> last = tail;
    // 将新插入的节点,作为尾节点
    tail = p;
    
    // 如果原先尾节点为null(原先链表为空,现在有一个元素)
    if (last == null)
        // 将p也作为头结点(只有一个元素的链表,head=tail=唯一的元素)
        head = p;
    else {
        // 链表有数据,则建立 前后 指向
        p.before = last;
        last.after = p;
    }
}

LinkedHashMap 通过新尾节点,给每个节点增加 before、after 属性,每次新增时,都把节点追加到尾节点等手段,在新增的时候,就已经维护了按照插入顺序的链表结构了。

遍历

LinkedHashMap可以通过以下,实现的Map接口中的方法,进行遍历。
image.png
当然,也可以通过迭代器来进行遍历。如map.entrySet().iterator();得到迭代器,再通过hasNext方法判断是否有下一个节点后,最后通过next来访问下一个节点。
其中的实现都依赖LinkedHashIterator。

final class LinkedEntryIterator extends LinkedHashIterator implements Iterator<Map.Entry<K,V>> {
    public final Map.Entry<K,V> next() { 
        return nextNode(); 
    }
}

// 下个节点
LinkedHashMap.Entry<K,V> next;
// 当前节点
LinkedHashMap.Entry<K,V> current;
// 期望的结构版本号
int expectedModCount;

// 构造方法
LinkedHashIterator() {
    // 初始化时,头结点即为 next
    next = head;
    expectedModCount = modCount;
    current = null;
}

// 是否还有下个节点
public final boolean hasNext() {
    return next != null;
}

// 获取下个节点
final LinkedHashMap.Entry<K,V> nextNode() {
    LinkedHashMap.Entry<K,V> e = next;
    
    // 判断结构版本号 是否有变更。有变更的话抛出错误
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    
    // 没有下个节点
    if (e == null)
        throw new NoSuchElementException();
    
    // 有下个节点,通过e.after,取得下个节点,并赋值给next
    current = e;
    next = e.after;
    
    // 返回当前节点
    return e;
}

LRU 最近最少使用

使用示例

这里,是采用了LinkedHashMap的三个参数的构造方法,其源码如下。其中最主要的,是指定了accessOrdertrue (默认为false)。 true 按照访问顺序,会把经常访问的 key 放到队尾,get时会造成结构的变更; false 按照插入顺序提供访问。

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

LRU大概的意思就是经常访问的元素会被追加到队尾,这样不经常访问的数据自然就靠近队头,然后我们可以通过设置删除策略,比如当 Map 元素个数大于3时。

public static void main(String[] args) {
    // 初始化时,主要指定了accessOrder为true
    LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>(16,0.75f,true) {

        @Override
        protected boolean removeEldestEntry(Map.Entry<String, Object> eldest) {
            return this.size() > 3;
        }
    };

    // 放入元素
    map.put("1",1);
    map.put("2",2);
    map.put("3",3);
    map.put("4",4);

    // 输出,并测试LRU策略
    System.out.println(map);
    map.get("2");
    System.out.println(map);
    map.get("3");
    System.out.println(map);
    map.get("4");
    System.out.println(map);
}
// {2=2, 3=3, 4=4}
// {3=3, 4=4, 2=2}
// {4=4, 2=2, 3=3}
// {2=2, 3=3, 4=4}

可以看到:我们往map中放置四个元素,但输出结果只有三个元素。1 不见了,这个是因为在每次put时,会调用afterNodeInsertion方法。该方法在允许删除节点时,并在removeEldestEntry方法返回true的情况下,(我们在用例中重写了removeEldestEntry方法)会移除头节点。源码如下:

void afterNodeInsertion(boolean evict) { // possibly remove eldest
    LinkedHashMap.Entry<K,V> first;
    if (evict && (first = head) != null && removeEldestEntry(first)) {
        K key = first.key;
        removeNode(hash(key), key, null, false, true);
    }
}

当我们调用get方法后,发现输出的元素顺序发生改变。被访问元素移动到链表尾部,这个体现了最经常被访问的节点会被移动到链表尾部。在调用get后,头节点即为最早插入-最少被访问的节点。

get方法源码

public V get(Object key) {
    Node<K,V> e;
    if ((e = getNode(hash(key), key)) == null)
        return null;
    
    // accessOrder为true时,才会去将访问的元素,放到链表尾部
    if (accessOrder)
        afterNodeAccess(e);
    return e.value;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LinkedHashMap常用方法源码 的相关文章

  • Java中ArrayList的交集和并集

    有什么方法可以做到这一点吗 我正在寻找 但没有找到 另一个问题 我需要这些方法 以便我可以过滤文件 有些是AND过滤器 有些是OR过滤器 就像集合论中的那样 所以我需要根据所有文件和保存这些文件的联合 相交 ArrayList 进行过滤 我
  • 在 Java 中克隆对象 [3 个问题]

    这样做会调用Asub的clone方法吗 或者Asub深度克隆是否正确 如果没有的话 有没有办法通过这种方法对Asub进行深度克隆呢 abstract class Top extends TopMost protected Object cl
  • 为什么 JTables 使 TableModel 在呈现时不可序列化?

    所以最近我正在开发一个工具 供我们配置某些应用程序 它不需要是什么真正令人敬畏的东西 只是一个具有一些 SQL 脚本生成功能并创建几个 XML 文件的基本工具 在此期间 我使用自己的 AbstractTableModel 实现创建了一系列
  • .properties 中的通配符

    是否存在任何方法 我可以将通配符添加到属性文件中 并且具有所有含义 例如a b c d lalalala 或为所有以结尾的内容设置一个正则表达式a b c anything 普通的 Java 属性文件无法处理这个问题 不 请记住 它实际上是
  • org.apache.hadoop.security.AccessControlException:客户端无法通过以下方式进行身份验证:[TOKEN,KERBEROS] 问题

    我正在使用 java 客户端通过 Kerberos 身份验证安全访问 HDFS 我尝试打字klist在服务器上 它显示已经存在的有效票证 我收到的异常是客户端无法通过以下方式进行身份验证 TOKEN KERBEROS 帮助将不胜感激 这是一
  • 如何获取之前的URL?

    我需要调用我的网络应用程序的 URL 例如 如果有一个从 stackoverflow com 到我的网站 foo com 的链接 我需要 Web 应用程序 托管 bean 中的 stackoverflow 链接 感谢所有帮助 谢谢 并不总是
  • 从最终实体获取根证书和中间证书

    作为密码学的菜鸟 我每天都会偶然发现一些简单的事情 今天只是那些日子之一 我想用 bouncy castle 库验证 java 中的 smime 消息 我想我几乎已经弄清楚了 但此时的问题是 PKIXparameters 对象的构建 假设我
  • 无法创建请求的服务[org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]-MySQL

    我是 Hibernate 的新手 我目前正在使用 Spring boot 框架并尝试通过 hibernate 创建数据库表 我知道以前也问过同样的问题 但我似乎无法根据我的环境找出如何修复错误 休眠配置文件
  • volatile、final 和synchronized 安全发布的区别

    给定一个带有变量 x 的 A 类 变量 x 在类构造函数中设置 A x 77 我们想将 x 发布到其他线程 考虑以下 3 种变量 x 线程安全 发布的情况 1 x is final 2 x is volatile 3 x 设定为同步块 sy
  • 如何在用户输入数据后重新运行java代码

    嘿 我有一个基本的java 应用程序 显示人们是成年人还是青少年等 我从java开始 在用户输入年龄和字符串后我找不到如何制作它它们被归类为 我希望它重新运行整个过程 以便其他人可以尝试 的节目 我一直在考虑做一个循环 但这对我来说没有用
  • 当 OnFocusChangeListener 应用于包装的 EditText 时,TextInputLayout 没有动画

    不能比标题说得更清楚了 我有一个由文本输入布局包裹的 EditText 我试图在 EditText 失去焦点时触发一个事件 但是 一旦应用了事件侦听器 TextInputLayout 就不再对文本进行动画处理 它只是位于 editText
  • 如何对不同的参数类型使用相同的java方法?

    我的问题 我有 2 个已定义的记录 创建对象请求 更新对象请求 必须通过实用方法进行验证 由于这两个对象具有相同的字段 因此可以对这两种类型应用相同的验证方法 现在我只是使用两种方法进行重载 但它很冗长 public record Crea
  • Java 和 Python 可以在同一个应用程序中共存吗?

    我需要一个 Java 实例直接从 Python 实例数据存储中获取数据 我不知道这是否可能 数据存储是否透明 唯一 或者每个实例 如果它们确实可以共存 都有其单独的数据存储 总结一下 Java 应用程序如何从 Python 应用程序的数据存
  • java for windows 中的文件图标叠加

    我正在尝试像 Tortoise SVN 或 Dropbox 一样在文件和文件夹上实现图标叠加 我在网上查了很多资料 但没有找到Java的解决方案 Can anyone help me with this 很抱歉确认您的担忧 但这无法在 Ja
  • Android:无法使用 DbHelper 和 Contract 类将数据插入 SQLite

    public class Main2Activity extends AppCompatActivity private EditText editText1 editText2 editText3 editText4 private Bu
  • Opencv Java 灰度

    我编写了以下程序 尝试从彩色转换为灰度 Mat newImage Imgcodecs imread q1 jpg Mat image new Mat new Size newImage cols newImage rows CvType C
  • 包 javax.el 不存在

    我正在使用 jre6 eclipse 并导入 javax el 错误 包 javax el 不存在 javac 导入 javax el 过来 这不应该是java的一部分吗 谁能告诉我为什么会这样 谢谢 米 EL 统一表达语言 是 Java
  • CamcorderProfile.videoCodec 返回错误值

    根据docs https developer android com reference android media CamcorderProfile html 您可以使用CamcorderProfile获取设备默认视频编解码格式 然后将其
  • Spring Boot 无法更新 azure cosmos db(MongoDb) 上的分片集合

    我的数据库中存在一个集合 documentDev 其分片键为 dNumber 样本文件 id 12831221wadaee23 dNumber 115 processed false 如果我尝试使用以下命令通过任何查询工具更新此文档 db
  • Java中super关键字的范围和使用

    为什么无法使用 super 关键字访问父类变量 使用以下代码 输出为 feline cougar c c class Feline public String type f public Feline System out print fe

随机推荐

  • Makefile的基本用法

    1 Makefile的基本概念 目标 目标顶格写 后面是冒号 冒号后面是依赖 依赖 依赖是用来产生目标的原材料 命令 命令前面一定是Tab 不能是顶格 也不能说多个空格 命令就是要生成那个目标需要做的动作 2 示例代码 led bin st
  • Node.js 连接 MongoDB

    在 Node js 中连接 MongoDB 数据库需要使用第三方模块 mongodb 首先需要安装 mongodb 模块 你可以使用 npm 命令来安装 npm install mongodb 接着可以使用以下代码来连接 MongoDB 数
  • Qt 可视化Ui设计

    QMainWindow 是主窗口类 主窗口类具有主菜单栏 工具栏和状态栏 类似于一般的应用程序的主窗口 QWidget是所有具有可视界面类的基类 选择QWidget创建的界面对各种界面组件都可以支持 QDialog是对话框类 可建立一个基于
  • 两种方法(JS方法和Vue方法)实现页面渲染

    一 需求 根据数据渲染如下页面 二 JS方法 div class box w div class box hd h3 精品推荐 h3 a href 查看全部 a div div class box bd ul class clearfix
  • JavaScript之ES6规范中let的巧用(经典案例讲解)

    html部分 ul li 天 li li 地 li li 人 li li 和 li ul ul li dyklk li ul 要求 再点击某个li标签的时候 弹框输出其对应的顺序号 注意 本文js代码部分全为原生写法 错误写法 var oL
  • 发布 VectorTraits v1.0,它是 C# 下增强SIMD向量运算的类库

    发布 VectorTraits v1 0 它是C 下增强SIMD向量运算的类库 VectorTraits SIMD Vector type traits methods SIMD向量类型的特征方法 NuGet https www nuget
  • 未能检测服务器连接失败,被控链接失败处理检查方法

    检查方法 1 重要 提示服务器连接失败 一般是防火墙问题或者主控与被控直接的通信问题 机器防火墙放行端口22333 1433 如有的服务器商的机器 需要在安全组里面放行端口 或者关闭防火墙 取消安全组 2 检查进程是否异常 右击任务栏 启动
  • C语言面试常撕的几个str代码-【strcpy】【memcpy】【strcmp】【memcpy】【strcat】

    一 字符串拷贝strcpy 代码 char strcpy char des const char src assert des NULL src NULL char p des while p src 0 return des 要注意的点
  • 什么是Scrum?如何实施Scrum(敏捷开发)以及敏捷工具

    什么是Scrum Scrum是一个敏捷开发框架 它是一个增量的 迭代的开发过程 它被广泛应用于敏捷软件开发 在Scrum中 开发过程由若干个短的迭代周期组成 每个迭代周期称为一个Sprint 那么Scrum如何实施呢 Scrum实施过程可分
  • idea使用分享

    ideaVim 配置文件 Source your vimrc source vimrc Suggested options Show a few lines of context around the cursor Note that th
  • 安卓自动化工具程序设计之[识别区域提取] python + uiautomator2 + Open CV

    安卓自动化工具程序设计之 识别区域提取 python uiautomator2 Open CV 一 设计需求 二 所需工具 三 程序设计过程与思路 四 工具使用讲解 五 程序源码 六 写在最后 一 设计需求 在安卓自动化控制中我们经常有需要
  • 健康体检中心

    传智健康 项目介绍 健康管理机构的业务系统 传统的互联网项目 后端系统 前端微信网页 开发人员应该需要的资料 1 需求说明书PRD 含功能大纲 功能详情 流程图 性能需求 产品原型图 2 UI 原型图并非最终效果图 最终要过要以UI为准 所
  • HTML 5概述

    HTML语言是一种简易的文件交换标准 用于物理的文件结构 它旨在定义文件内的对象和描述文件的逻辑结构 而并不定义文件的显示 由于HTML所描述的文件具有极高的适应性 所以特别适合于WWW的出版环境 什么是 HTML5 HTML5是HTML语
  • css选择某元素优先上方显示的方法

    z index 在做一个搜索栏时想要在搜索栏上显示文字提示 当然不是placeholder那种 是一种浮动在搜索栏上固定的元素提供的文本文字 例如这种 查找资料后知道用的是z index 这里是z index的MDN手册说明 z index
  • c语言 结构体排序 相同 下一个,C语言 · 运用结构体的排序方法

    之前遇到排序只想着最原始的方法 诸如冒泡 选择 快速排序等等 刚刚跟大牛学会了结构体的方法来排序 这样的话以后再也不用怕成绩统计 名次排序之类的题目了 首先头文件 基于大牛的方法 本人之后做题喜欢引入题目中常用的五个头文件 定义结构体 注释
  • Java架构师面试题全集:Java基础+技术框架+系统架构+分布式系统

    Java架构师面试题全集 Java基础 技术框架 系统架构 分布式系统 优知学院 2018 10 10 18 45 00 基础题目 Java线程的状态 进程和线程的区别 进程间如何通讯 线程间如何通讯 HashMap的数据结构是什么 如何实
  • excel表格中忘了撤销工作表保护密码怎么办

    转自 https zhidao baidu com question 297630230 html 用宏代码破解密码 以office2007为例说明 2003也是一样的 只是菜单命令的位置不同 第一步 打开该文件 先解除默认的 宏禁用 状态
  • 深入解析QUIC协议

    QUIC Quick UDP Internet Connection 是Google提出的一个基于UDP的传输协议 因其高效的传输效率和多路并发的能力 已经成为下一代互联网协议HTTP 3的底层传输协议 除了应用于Web领域 它的优势同样适
  • 2023前端面试题

    HTML CSS 1 块元素垂直居中 1 弹性布局 display flex justify content center align items center 2 定位 position absolute left 50 top 50 t
  • LinkedHashMap常用方法源码

    类介绍 注释 add contains remove 方法 时间复杂度是O 1 LinkedHashMap的遍历耗时 与 capacity无关 与map的size 元素多少 呈线性 HashMap的遍历 可能比 LinkedHashMap更