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常用方法源码 的相关文章

随机推荐

  • 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更