LinkedHashMap-程序员宅基地

技术标签: 源码阅读--集合  

概念

LinkedHashMap继承自HashMap,它的结构如图所示:
在这里插入图片描述

  • hashmap是无序的,LinkedHashMap是有序的,且默认为插入顺序。

  • LinkedHashMap通过在HashMap的基础上增加一条双向链表,实现了插入顺序和访问顺序一致。通过对HashMap一些方法的覆盖,例如newNode, replacementNode, replacementTreeNode, newTreeNode,让所有对底层HashMap数据结构修改的同时该链表进行修改,遍历的时候便是遍历这一条有序的链表。需要注意的是get()方法在accessOrder为true的时候也会对底层结构进行修改。

  • 基于get()在accessOrder为true时,会将访问到的元素放到链表的最后的特性,我们可以使用LinkedHashMap实现LRU缓存

  • LinkedHashMap和HashMap一样,线程不安全。

  • 另外,leetcode中剑指 Offer 50. 第一个只出现一次的字符 也使用到了这个数据结构。

  • 关于访问顺序和插入顺序, 最开始都是按照插入顺序排列的,而如果设置了accessOrder = true, 则在调用get()方法时,会因为 if(accessOrder == true ) 而调用afterNodeAccess方法来调整顺序。

源码分析

get

和hashmap相比,只是在后面增加了if (accessOrder) 代码,目的是实现 LinkedHashMap 按照访问顺序排序。

另外,没有重写getNode()方法,和hashmap一样。

public V get(Object key) {
    
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
            afterNodeAccess(e);
        return e.value;
    }

afterNodeAccess

就是将一个结点移动到链表尾部

    void afterNodeAccess(Node<K,V> e) {
     // move node to last
        LinkedHashMap.Entry<K,V> last;
        if (accessOrder && (last = tail) != e) {
    
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.after = null;
          
          // 如果e为头结点
            if (b == null)
                head = a;
            else
                b.after = a;
          // 如果e为尾结点
            if (a != null)
                a.before = b;
            else
                last = b;
         // 如果链表为空
            if (last == null)
                head = p;
            else {
    
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }

-------------------

put

LinkedHashMap没有对put和putVal()进行重写,但是对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;
    }

linkNodeLast

private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
    
        LinkedHashMap.Entry<K,V> last = tail;
        tail = p;
  		// 如果链表为空,则设置p为头部
        if (last == null)
            head = p;
      // 不为空,则放到尾部
        else {
    
            p.before = last;
            last.after = p;
        }
    }

--------------------

remove

LinkedHashMap也没有重写remove()方法,而是重写了removeNode()方法里的afterNodeRemoval()方法

afterNodeRemoval

removeNode()方法中只是将HashMap.Entry的next指针删掉, 而 LinkedHashMap.Entry的 before和after指针还没删除,因此这个方法就是断掉双向链表里的after和before

应用

LRU

获取LinkedHashMap的头部元素

时间复杂度O(1)

public <K, V> Entry<K, V> getHead(LinkedHashMap<K, V> map) {
    
    return map.entrySet().iterator().next();
}

获取LinkedHashMap的尾部元素

  • 利用迭代器,时间复杂度为 O(n)
public <K, V> Entry<K, V> getTail(LinkedHashMap<K, V> map) {
    
    Iterator<Entry<K, V>> iterator = map.entrySet().iterator();
    Entry<K, V> tail = null;
    while (iterator.hasNext()) {
    
        tail = iterator.next();
    }
    return tail;
}
  • 利用反射,时间复杂度为O(1)
	public static  <K, V> Map.Entry<K, V> getTail(LinkedHashMap<K, V> map) throws NoSuchFieldException, IllegalAccessException {
    
		Class<? extends LinkedHashMap> aClass = map.getClass();
		Field tail = aClass.getDeclaredField("tail");
		tail.setAccessible(true);
		return (Map.Entry<K, V>)tail.get(map);
	}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_36409043/article/details/113992605

智能推荐

vscode-设置快捷模板_vscode 设置 快捷模板-程序员宅基地

文章浏览阅读452次,点赞12次,收藏6次。当要使用模板时,输入"prefix"里自定义的口令,比如上面的口令是vh,输入vh再按enter即可。2.输入 configure user snippets / 配置用户代码片段。3.选择Add browser Breakpoint / 新建全局代码片段文件。4.自定义json文件名,例如vue3.json。将以下内容复制进{}里,保存即可。_vscode 设置 快捷模板

零基础 ESP-01S使用AT指令连接阿里云(含ESP-01S 固件烧录)_at+cipsntpcfg-程序员宅基地

文章浏览阅读889次,点赞11次,收藏16次。本教程从ESP8266-01S固件烧录开始(保姆级教程),详细介绍了如何使用AT指令将ESP8266-01S连接到阿里云物联网平台。对于零基础的用户来说,只要按照教程操作,就能够轻松实现物联网设备的接入和控制。_at+cipsntpcfg

js如何封装一个函数,判断数据是否是数组_封装一个工具函数,传入一个数据,判断是否是数组(异常处理,提升代码健壮性),对传入-程序员宅基地

文章浏览阅读338次。第一种,Array.isArray(参数) <script> // Array.isArray(arr) // 函数名- isArray function isArray(arr) { if (Array.isArray(arr)) { console.log('是一个数组'); } else { console.log('不是一个数组'); } } let arr1 = 'a'_封装一个工具函数,传入一个数据,判断是否是数组(异常处理,提升代码健壮性),对传入

基于vue、avue、element-ui的前端框架saber的顶部菜单栏配置_saber 新增菜单-程序员宅基地

文章浏览阅读4.5k次,点赞3次,收藏10次。步骤1:文件位置:Saber/src/mock/menu.js新增菜单模块:步骤2:绑定对应模块路由:(与步骤1中的模块菜单名称对应)在zh.js中添加新增菜单的名称:(与对应路由名称绑定)新建一个页面,路径与步骤1中页面路径对应 (Saber/src/views/util/test.vue),编写页面:完成效果图如下:附上test.vue的完整测试代码:<t..._saber 新增菜单

centos如何安装libssl-dev libsdl-dev libavcodec-dev libavutil-dev ffmpeg_yum install libssl-dev-程序员宅基地

文章浏览阅读3.3k次,点赞2次,收藏2次。安装完成后,这些包将会被安装在您的 CentOS 系统上。打开终端,使用 root 或具有管理员权限的用户登录。_yum install libssl-dev

Seaborn常见绘图总结_seaborn清晰度-程序员宅基地

文章浏览阅读747次。以前粗略的学习过Matplotlib绘图、Pandas绘图(这里是pandas的常见绘图总结),但是都未深入的去学习过,一遇到问题就翻..._seaborn清晰度

随便推点

python二分查找算法注释_Python如何实现的二分查找算法-程序员宅基地

文章浏览阅读76次。先来看个用Python实现的二分查找算法实例import sysdef search2(a,m):low = 0high = len(a) - 1while(low <= high):mid = (low + high)/2midval = a[mid]if midval < m:low = mid + 1elif midval > m:high = mid - 1else:pr..._print(binary_search(0, len(list_num)-1,target, list_num))

Android的RecyclerView使用介绍_androidx.recyclerview:recyclerview:1.1.0-程序员宅基地

文章浏览阅读400次。1、首先要在Gradle 里面设置dependencies2、在UI XML里面添加RecyclerView3、需要编写一个RecyclerView.Adapter 3.1 Adapter里面要编写RecyclerView.Viewholder: 这是RV里面显示的小单元_androidx.recyclerview:recyclerview:1.1.0

CKEditor5上传不了图片问题(简单易懂版)_vue+ckeditor上传图片不显示-程序员宅基地

文章浏览阅读3.3k次。1、最近想用下CKEditor5 新特性,但发现上传不上去,经调研官网调研如下:1、首先引入jshttps://cdn.ckeditor.com/ckeditor5/17.0.0/classic/ckeditor.js2、html相关代码<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><..._vue+ckeditor上传图片不显示

matplotlib图表嵌入pyqt5_alphalens 图表显示在pyqt中-程序员宅基地

文章浏览阅读1.9k次,点赞2次,收藏24次。1、在Qt designer中设计界面,包含一个QGroupBox和QWidget:2、导入需要的库:import matplotlibmatplotlib.use("Qt5Agg") # 声明使用QT5from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvasimport matplotlib.pyplot as plt3、编写绘图函数:def plot_figure(self, ax_alphalens 图表显示在pyqt中

docker 搭建自己的gitlab_gitlab 搭建 docker-程序员宅基地

文章浏览阅读97次。docker 搭建自己的gitlabhttps://www.cnblogs.com/smallstudent/p/5396660.html--privileged=true 容器拥有root权限172.17.0.8--hostname 111.229.235.9 服务器ip地址docker pull gitlab/gitlab-ce:8.7.0-rc3.ce.0mkdir -p /opt/gitlab/config/opt/gitlab/logs/opt..._gitlab 搭建 docker

达梦数据库的安装与使用_mac安装达梦数据库-程序员宅基地

文章浏览阅读5.7k次,点赞4次,收藏62次。达梦数据库使用安装数据库一、安装规划1、创建用户组、用户、安装目录[root@Kylin10 ~]# groupadd dinstall[root@Kylin10 ~]# useradd -g dinstall -m -d /home/dmdba -s /bin/bash dmdba[root@Kylin10 ~]# passwd dmdba 更改用户 dmdba 的密码 。新的 密码:重新输入新的 密码:passwd:所有的身份验证令牌已经成功更新。[root@Kylin10 ~]#_mac安装达梦数据库

推荐文章

热门文章

相关标签