一、概述

LinkedHashMap继承自HashMap,是Map接口的一个具体实现,它是有序的,可以按照插入顺序先后和访问时间先后进行排序,选择哪种排序方式取决于在新建LinkedHashMap的时候是否指定了accessOrder为true。如果不指定,accessOrder默认为false,LinkedHashMap会按照插入顺序进行排序;入股指定为true,则会按照LRU算法将最少访问的元素排在前面。

LinkedHashMap中的removeEldestEntry(Map.Entry)方法默认返回false,如果重写,如:

private static final int MAX_ENTRIES = 100;

protected boolean removeEldestEntry(Map.Entry eldest) {

return size() > MAX_ENTRIES;

}

,则当节点达到100,再插入新节点就会删除过时的链表头部的节点。

LinkedHashMap提供了所有可选的Map操作,并允许插入null。因为要多维护一个链表,其性能可能略低于HashMap。但是,有一个例外:对LinkedHashMap的集合视图的迭代需要时间与map大小成正比,而不管其容量。 HashMap上的迭代可能会花销更大,需要的时间与其容量成正比。也就是说capacity 的大小对其性能影响没有对HashMap的大。

LinkedHashMap的实现是非同步的。当涉及到多个线程访问并且有线程改变其结构时,必须在外部进行同步。通常对使用map的对象进行同步来完成同步操作。如果不存在这样的对象,则应该使用Collections.synchronizedMap方法包裹该map。而且最好在创建时包裹,以防止不同步访问:

Map m = Collections.synchronizedMap(new LinkedHashMap(...));

这里,需要注意的是map节点的值改变不会导致结构改变,结构改变是指添加、删除节点或者修改顺序引起的改变。

LinkedHashMap继承了HashMap的fail-fast(快速失败)机制,该机制的实现是通过一个modCount变量来记录修改次数,在每次调用会导致LinkedHashMap结构改变的方法的时候就比较下理论上的修改次数和当前修改次数的值。如果相等则允许操作,并将modCount加1,如果不相等则抛出ConcurrentModificationException异常。这种机制只能用来检测错误,无法保证一定起效。

二、结构

LinkedHashMap之所以能够进行排序,是因为它在HashMap的“数组+链表”的结构上还维护了一个双向链表,其节点结构可以参考如下的图(这里重点关注before和after节点):

(图1)

整体结构可以参考如下的图(图上半部分的“数组+链表”结构继承自HashMap,绿色的箭头代表图1中的next,排序的实现主要通过下面的双向链表):

(图2来自:http://blog.csdn.net/luanlouis/article/details/43017071

三、源码分析

构造函数:

这里注意到第四个构造函数,如果指定第三个参数为true会开启按访问时间排序。另外几个构造函数都有设置accessOrder = false。

属性:

accessOrder已经说明过了。剩下的,head代表的是双向链表的头部,表示最久未被访问的节点。tail代表的是双向链表的尾部,表示最近一次访问的节点。

方法:

这里可以重点看下afterNodeAccess方法,调用put,putAll,putIfAbsent,get,getOrDefault,compute,computeIfAbsent,computeIfPresent或merge方法时都将调用该方法。而调用该方法时,如果accessOrder为true,且当前操作的节点在方法调用后存在,则会移动当前操作的节点至双向链表尾部。

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;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}

其他方法介绍可参考:

http://blog.csdn.net/u014634338/article/details/78494051

四、总结

LinkedHashMap继承于 HashMap,相当于在HashMap的基础上添加了顺序访问的功能,而方法的操作也是基于HashMap之上,增加了对提供顺序访问功能的双向链表的维护。

LinkedHashMap学习的更多相关文章

  1. Java学习之LinkedHashMap学习总结

    前言: 在学习LRU算法的时候,看到LruCache源码实现是基于LinkedHashMap,今天学习一下LinkedHashMap的好处以及如何实现lru缓存机制的. 需求背景: LRU这个算法就是 ...

  2. Java LinkedHashMap学习

    以前一直使用HashMap,今天学习一下LinkedHashMap JavaDoc 注解: Hash table and linked list implementation of the Map i ...

  3. HashMap,TreeMap,LinkedHashMap学习

    Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复.Hashmap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快 ...

  4. java集合类学习笔记之LinkedHashMap

    1.简述 LinkedHashMap是HashMap的子类,他们最大的不同是,HashMap内部维护的是一个单向的链表数组,而LinkedHashMap内部维护的是一个双向的链表数组.HashMap是 ...

  5. JDK源码学习笔记——LinkedHashMap

    HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序. LinkedHashMap保证了元素迭代的顺序.该迭代顺序可以是插入顺序或者是访问顺序.通过维护一个 ...

  6. 【Java学习笔记】HashMap子接口---LinkedHashMap

    特点: 存入元素的顺序   与   取出元素的顺序相同(与LinkedHashSet类似) import java.util.HashMap; import java.util.Iterator; i ...

  7. LinkedHashMap源码学习

    描述 可以按照添加元素的顺序对元素进行迭代的HashMap的子类. 注意,上面说的是加元素的顺序.也就是说,更新元素时,是不会影响遍历结构的的.除非设置参数accessOrder为true,将更新元素 ...

  8. Java集合学习(5):LinkedHashMap

    一.概述 HashMap是无序的,HashMap在put的时候是根据key的hashcode进行hash然后放入对应的地方.所以在按照一定顺序put进HashMap中,然后遍历出HashMap的顺序跟 ...

  9. 学习JDK1.8集合源码之--LinkedHashMap

    1. LinkedHashMap简介 LinkedHashMap继承自HashMap,实现了Map接口. LinkedHashMap是HashMap的一种有序实现(多态,HashMap的有序态),可以 ...

随机推荐

  1. Java中关于内存泄漏出现的原因以及如何避免内存泄漏

    转账自:http://blog.csdn.net/wtt945482445/article/details/52483944 Java 内存分配策略 Java 程序运行时的内存分配策略有三种,分别是静 ...

  2. 将安防IPC摄像机进行类似于萤石/乐橙/360水滴模式的互联网直播的几种方案

    前言 在维护EasyDarwin开源项目的几年内,几乎市面上大大小小的技术需求都给接触了一遍,大团队.大背景有大需求,草根团队有草根团队的需求,然而这些需求近些年都有一个发展的趋势,那就是" ...

  3. 利用sklearn的LabelEncoder对标签进行数字化编码

    from sklearn.preprocessing import LabelEncoder def gen_label_encoder(): labels = ['BB', 'CC'] le = L ...

  4. Python的安装与设置

    1.Python的下载与安装最新的python 版本下载可以去python的网站进行下载 . 考虑系统兼任这里下载32位的Python 双击下载的exe文件进行安装 单击Next 完成Python 安 ...

  5. linux下升级svn版本到1.8

    CentOS6.5默认yum安装的svn版本为1.6,有时候遇到比较高级的应用就可能不够使用,这时候就需要升级一下svn的版本,可以升级到的版本为1.8 ====== 完美的分割线 ====== 1. ...

  6. “App Store加载失败,使已购页面再试一次”解决方案

    问题描述: 用A账户登陆App Store,下载了Xcode.还没有下载完就需要更换账户 更换账户 找到App Store界面上部的商店,选择注销,然后再登陆. 账户更换完毕,讲道理来说应该是可以下载 ...

  7. BZOJ4837:[Lydsy1704月赛]LRU算法(双指针&模拟)

    Description 小Q同学在学习操作系统中内存管理的一种页面置换算法,LRU(LeastRecentlyUsed)算法. 为了帮助小Q同学理解这种算法,你需要在这道题中实现这种算法,接下来简要地 ...

  8. 每天一个linux命令:【转载】cd命令

    Linux cd 命令可以说是Linux中最基本的命令语句,其他的命令语句要进行操作,都是建立在使用 cd 命令上的. 所以,学习Linux 常用命令,首先就要学好 cd 命令的使用方法技巧. 1.  ...

  9. Uoj 129 寿司晚宴

    Uoj 129 寿司晚宴 显然合法性只与每个数所含的质因子有关,考虑状压 \(dp\) 若记录所有质因子状态显然爆炸,注意到每个数最多有一个超过 \(\sqrt 500\) 的大质因子,而其他的小质因 ...

  10. 使用 GitVersion 在编译或持续构建时自动使用语义版本号(Semantic Versioning)

    我们在之前谈过 语义版本号(Semantic Versioning),在项目中应用语义版本号能够帮助库的开发者在发布包时表明更多的语义信息.这是趋势,从微软的博客 Versioning NuGet p ...