摘要: LinkedHashMap继承了HashMap,他在HashMap的基础上增加了一个双向链表的结构,链表默认维持key插入的顺序,重复的key值插入不会改变顺序,适用于使用者需要返回一个顺序相同的map对象的情况。

LinkedHashMap继承了HashMap,他在HashMap的基础上增加了一个双向链表的结构,链表默认维持key插入的顺序,重复的key值插入不会改变顺序,适用于使用者需要返回一个顺序相同的map对象的情况。还可以生成access-order顺序的版本,按照最近访问顺序来存储,刚被访问的结点处于链表的末尾,适合LRU,put get compute merge都算作一次访问,其中put key值相同的结点也算作一次访问,replace只有在换掉一个键值对的时候才算一次访问,putAll产生的访问顺序取决于原本map的迭代器实现。

在插入键值对时,可以通过对removeEldestEntry重写来实现新键值对插入时自动删除最旧的键值对

拥有HashMap提供的方法,迭代器因为是通过遍历双向链表,所以额外开销与size成正比与capacity无关,因此选择过大的初始大小对于遍历时间的增加没有HashMap严重,后者的遍历时间依赖与capacity。

同样是非线程安全方法,对于LinkedHashMap来说,修改结构的操作除了增加和删除键值对外,还有对于access-order时进行了access导致迭代器顺序改变,主要是get操作,对于插入顺序的来说,仅仅修改一个已有key值的value值不是一个修改结构的操作,但对于访问顺序,put和get已有的key值会改变顺序。迭代器也是fail-fast设计,但是fail-fast只是一个调试功能,一个设计良好的程序不应该出现这个错误

因为HashMap加入了TreeNode,所以现在LinkedHashMap也有这个功能

以下描述中的链表,若无特别说明都是指LinkedHashMap的双向链表

先来看一下基本结构,每个键值对加入了前后指针,集合加入了头尾指针来形成双向链表,accessOrder代表链表是以访问顺序还是插入顺序存储

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);}}/*** The head (eldest) of the doubly linked list.头部*/transient LinkedHashMap.Entry<K,V> head;/*** The tail (youngest) of the doubly linked list.尾部*/transient LinkedHashMap.Entry<K,V> tail;//true访问顺序 false插入顺序final boolean accessOrder;
复制代码

然后是几个内部方法。linkNodeLast将p连接到链表尾部

 private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {LinkedHashMap.Entry<K,V> last = tail;tail = p;if (last == null)head = p;//原本链表为空则p同时为头部else {p.before = last;last.after = p;}}
复制代码

transferLinks用dst替换src

 private void transferLinks(LinkedHashMap.Entry<K,V> src,LinkedHashMap.Entry<K,V> dst) {LinkedHashMap.Entry<K,V> b = dst.before = src.before;LinkedHashMap.Entry<K,V> a = dst.after = src.after;if (b == null)head = dst;elseb.after = dst;if (a == null)tail = dst;elsea.before = dst;}
复制代码

reinitialize在调用HashMap方法的基础上,将head和tail设为null

 void reinitialize() {super.reinitialize();head = tail = null;}
复制代码

newNode生成一个LinkedHashMap结点,next指向e,插入到LinkedHashMap链表末端

 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);//新建一个键值对,next指向elinkNodeLast(p);//p插入到LinkedHashMap链表末端return p;}
复制代码

replacementNode根据原结点生成一个LinkedHashMap结点替换原结点

 Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;LinkedHashMap.Entry<K,V> t =new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);//生成一个新的键值对next是给出的next参数transferLinks(q, t);//用t替换qreturn t;}
复制代码

newTreeNode生成一个TreeNode结点,next指向next,插入到LinkedHashMap链表末端

 TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);//生成一个TreeNode,next指向参数nextlinkNodeLast(p);//p插入到LinkedHashMap链表末端return p;}
复制代码

replacementTreeNode根据结点p生成一个新的TreeNode,next设为给定的next,替换原本的p

 TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);transferLinks(q, t);//根据结点p生成一个新的TreeNode,next设为给定的next,替换原本的preturn t;}
复制代码

afterNodeRemoval从LinkedHashMap的链上移除结点e

 void afterNodeRemoval(Node<K,V> e) { LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;p.before = p.after = null;if (b == null)head = a;elseb.after = a;if (a == null)tail = b;elsea.before = b;}
复制代码

afterNodeInsertion可能移除最旧的结点,需要evict为true同时链表不为空同时removeEldestEntry需要重写

 void afterNodeInsertion(boolean evict) { LinkedHashMap.Entry<K,V> first;if (evict && (first = head) != null && removeEldestEntry(first)) {//removeEldestEntry需要重写才从发挥作用,否则一定返回falseK key = first.key;//移除链表头部的结点removeNode(hash(key), key, null, false, true);}}
复制代码

afterNodeAccess在访问过后将结点e移动到链表尾部,需要Map是access-order,若移动成功则增加modCount

 void afterNodeAccess(Node<K,V> e) { LinkedHashMap.Entry<K,V> last;if (accessOrder && (last = tail) != e) {//Map是access-order同时e不是链表的尾部LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;p.after = null;if (b == null)//将结点e从链表中剪下head = a;elseb.after = a;if (a != null)a.before = b;elselast = b;if (last == null)head = p;else {p.before = last;last.after = p;}tail = p;//结点e移动到链表尾部++modCount;//因为有access-order下结点被移动,所以增加modCount}}
复制代码

构造函数方面,accessOrder默认是false插入顺序,初始大小为16,负载因子为0.75,这里是同HashMap。复制构造也是调用了HashMap.putMapEntries方法

containsValue遍历链表寻找相等的value值,这个操作一定不会造成结构改变

 public boolean containsValue(Object value) {for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {//检查同样是根据LinkedHashMap提供的链表顺序进行遍历V v = e.value;if (v == value || (value != null && value.equals(v)))return true;}return false;}
复制代码

get方法复用HashMap的getNode方法,若找到结点且Map是访问顺序时,要将访问的结点放到链表最后,若没找到则返回null。而getOrDefault仅有的区别是没找到时返回defaultValue

 public V get(Object key) {Node<K,V> e;if ((e = getNode(hash(key), key)) == null)//复用HashMap的getNode方法return null;if (accessOrder)afterNodeAccess(e);//access-order时将e放到队尾return e.value;}public V getOrDefault(Object key, V defaultValue) {Node<K,V> e;if ((e = getNode(hash(key), key)) == null)return defaultValue;//复用HashMap的getNode方法,若没有找到对应的结点则返回defaultValueif (accessOrder)afterNodeAccess(e);//access-order时将e放到队尾return e.value;}
复制代码

clear方法在HashMap的基础上要把head和tail设为null

 public void clear() {super.clear();head = tail = null;}
复制代码

removeEldestEntry在put和putAll插入键值对时调用,原本是一定返回false的,如果要自动删除最旧的键值对要返回true,需要进行重写。比如下面这个例子,控制size不能超过100

 private static final int MAX_ENTRIES = 100;protected boolean removeEldestEntry(Map.Entry eldest) {return size() > MAX_ENTRIES;}
复制代码

下面两个方法和HashMap相似,返回key的Set和value的Collection还有返回键值对的Set,这个是直接引用,所以对它们的remove之类的修改会直接反馈到LinkedHashMap上

 public Set<K> keySet() {Set<K> ks = keySet;if (ks == null) {ks = new LinkedKeySet();keySet = ks;}return ks;//返回key值的set}public Collection<V> values() {Collection<V> vs = values;if (vs == null) {vs = new LinkedValues();values = vs;}return vs;//返回一个包含所有value值的Collection}public Set<Map.Entry<K,V>> entrySet() {Set<Map.Entry<K,V>> es;return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;//返回一个含有所有键值对的Set}
复制代码

检查HashMap的putVal方法,我们可以看到在找到了相同key值并修改value值时会调用afterNodeAccess,对于access-order会改变结点顺序

 if (e != null) { // 找到了相同的key则修改value值并返回旧的valueV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}复制代码

Java LinkedHashMap类源码解析相关推荐

  1. Java String类源码解析

    String直接继承Object 含有一个char[] value,还有一个int hash默认值为0 new String()的构造产生的是一个值为""的字符数组 String( ...

  2. Java Optional类源码解析和用法

    个人网站:http://xiaocaoshare.com/ 要求:jdk1.8以上 Optional类是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get( ...

  3. java.lang 源码剖析_java.lang.Void类源码解析

    在一次源码查看ThreadGroup的时候,看到一段代码,为以下: /* * @throws NullPointerException if the parent argument is {@code ...

  4. Java String类源码阅读笔记

    文章目录 一.前置 二.String类源码解析 1.String类继承关系 2.成员变量 3.构造方法 4.长度/判空 5.取字符 6.比较 7.包含 8.hashCode 9.查询索引 10.获取子 ...

  5. Java集合---LinkedList源码解析

    一.源码解析 1. LinkedList类定义 2.LinkedList数据结构原理 3.私有属性 4.构造方法 5.元素添加add()及原理 6.删除数据remove() 7.数据获取get() 8 ...

  6. Java线程池源码解析及高质量代码案例

    引言 本文为Java高级编程中的一些知识总结,其中第一章对Jdk 1.7.0_25中的多线程架构中的线程池ThreadPoolExecutor源码进行架构原理介绍以及源码解析.第二章则分析了几个违反J ...

  7. Java集合-ArrayList源码解析-JDK1.8

    ◆ ArrayList简介 ◆ ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了List, RandomAcc ...

  8. Node 学习六、核心模块 events之 01 events 和 EventEmitter 类、发布订阅、EventEmitter 类源码解析和模拟实现

    events 事件模块 events 与 EventEmitter node.js 是基于事件驱动的异步操作架构,内置 events 模块 events 模块提供了 EventEmitter 类 这个 ...

  9. Scroller类源码解析及其应用(一)

    滑动是我们在自定义控件时候经常遇见的难题,让新手们倍感困惑,这篇文章主要介绍Scroller类的源码,告诉打击这个到底有什么用,怎么使用它来控制滑动.另外,我还会结合一个简单的例子,来看一下这个类的应 ...

最新文章

  1. 代码实现UISlider 和 UISwitch
  2. 数据中心网络架构 — 云数据中心网络 — SDN 网络技术
  3. jvm 9种垃圾收集器
  4. 【图解】一图了解《上海市推进新一代信息基础设施建设 助力提升城市能级和核心竞争力三年行动计划(2018-2020年)》...
  5. 数据结构之单链表(头结点)的一些常用操作(增删改查逆)
  6. linux高亮查找关键字
  7. mysql数据库表的类型介绍,mysql数据库表的类型介绍
  8. aptana php 调试,aptana 3 + xdebug php
  9. Windows Server上安装部署Zabbix agent
  10. python可以代替按键精灵吗_Python 假装自己是按键精灵
  11. 使用bus hound发送包的方法
  12. Ubuntu 16.04 安装SecureCRT破解版
  13. python根据图片网址下载图片
  14. c++ STL容器 --- 栈stack
  15. 新闻推荐系统【DAY1】
  16. 一些简单的shell实例
  17. 小明的调查作业java_小明的调查作业
  18. springmvc——万事开头难
  19. 神经网络专业硕士就业,神经科学专业就业方向
  20. matlab圆周率计算,matlab代码求圆周率的简单算法

热门文章

  1. HTTP协议和HTTPS协议初探
  2. java web 播放音频_使用Java ME以流形式播放Web服务器上的音乐文件
  3. Java-ThreadLocal三种使用场景
  4. python 左旋转字符串
  5. python地理数据处理 下载_python-doc/将Python用于地理空间数据处理.md at master · zhuxinyizhizun/python-doc · GitHub...
  6. python 文件末尾增加内容_java追加内容到文件末尾
  7. android 创建选项卡,Android用Fragment创建选项卡
  8. linux vi/vim使用
  9. 河马php一句话木马,一句话木马的套路
  10. linux下的geany源码安装,linux 下php开发工具geany-0.16的安装。