Java集合框架:LinkedHashMap
欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。
欢迎跳转到本文的原文链接:https://honeypps.com/java/java-collection-linkedhashmap/
如无特殊说明,本文以jdk7为准进行说明。
package java.util;
import java.io.*;
public class LinkedHashMap<K,V>extends HashMap<K,V>implements Map<K,V>{
}
可以看到LinkedHashMap继承了HashMap,那么LinkedHashMap又有什么特点呢?
LinkedHashMap是Hash表和链表的实现,并且依靠着双向链表保证了迭代顺序是插入的顺序。非线程安全。
首先来段代码(与HashMap那篇类似):
Map<String,Integer> map = new LinkedHashMap<>();map.put("s1", 1);map.put("s2", 2);map.put("s3", 3);map.put("s4", 4);map.put("s5", 5);map.put(null, 9);map.put("s6", 6);map.put("s7", 7);map.put("s8", 8);map.put(null, 11);for(Map.Entry<String,Integer> entry:map.entrySet()){System.out.println(entry.getKey()+":"+entry.getValue());}System.out.println(map);
输出结果:
s1:1
s2:2
s3:3
s4:4
s5:5
null:11
s6:6
s7:7
s8:8
{s1=1, s2=2, s3=3, s4=4, s5=5, null=11, s6=6, s7=7, s8=8}
通过结果可以看到,LinkedHashMap会记录插入的顺序,允许null的键值,当key值重复时,后面的会替换前面的。
通过工作看下LinkedHashMap的结构图(ppt画图不容易,求点赞~)
可以看到LinkedHashMap比HashMap多了一个头指针head(private权限),header指针是一个标记指针不存储任何数据。标记after和before两个指针。
可以看到bullet的实体Entry也比HashMap的Entry多了before和after两个指针。(private static class Entry<K,V> extends HashMap.Entry<K,V>{Entry<K,V> before, after;} )
LinkedHashMap还有一个私有变量accessOrder(private final boolean accessOrder;),默认为false,即按照插入顺序遍历,譬如开篇的例子中,如果设置为true则按照访问顺序遍历,只能通过这个构造函数设置:
public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {super(initialCapacity, loadFactor);this.accessOrder = accessOrder;}
接下来举个例子(和开篇的例子类似):
Map<String,Integer> map = new LinkedHashMap<>(16,0.75f,true);map.put("s1", 1);map.put("s2", 2);map.put("s3", 3);map.put("s4", 4);map.put("s5", 5);map.put(null, 9);map.put("s6", 6);map.put("s7", 7);map.put("s8", 8);map.put(null, 11);map.get("s6");for(Map.Entry<String,Integer> entry:map.entrySet()){System.out.println(entry.getKey()+":"+entry.getValue());}
输出结果:
s1:1
s2:2
s3:3
s4:4
s5:5
s7:7
s8:8
null:11
s6:6
可以看到遍历顺序改为了访问顺序。
如果将上面的遍历方式改为:
for(Iterator<String> iterator = map.keySet().iterator();iterator.hasNext();){String name = iterator.next();System.out.println(name+"->"+map.get(name));}
运行结果出人意料:
s1->1
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.LinkedHashMap$LinkedHashIterator.nextEntry(Unknown Source)at java.util.LinkedHashMap$KeyIterator.next(Unknown Source)at collections.map.LinkedHashMapTest.main(LinkedHashMapTest.java:33)
LinkedHashMap非但没有排序,反而程序出现了异常,这是为什么呢?
ConcurrentModificationException异常一般会在集合迭代过程中被修改事抛出。不仅仅是LinkedHashMap,所有的集合都不允许在迭代器模式中修改集合的结构。一般认为,put()、remove()方法会修改集合的结构,因此不能在迭代器中使用。但是,这段代码中并没有出现类似修改集合结构的代码,为何也会发生这样的问题?
问题就出在get()方法上。虽然一般认为get()方法是只读的,但是当前的LinkedHashMap缺工作在按照元素访问顺序排序的模式中,get()方法会修改LinkedHashMap中的链表结构,以便将最近访问的元素放置到链表的末尾,因此,这个操作便引起了这个错误。所以,当LinkedHashMap工作在这个模式时,不能再迭代器中使用get()操作。Map的遍历建议使用entrySet的方式。
>不要在迭代器模式中修改被迭代的集合。如果这么做,就会抛出ConcurrentModificationException异常。这个特性适用于所有的集合类,包括HashMap,Vector,ArrayList等。
LinkedHashMap可以根据访问顺序排序。那这个功能有什么牛逼之处呢?提示一下:LRU
LinkedHashMap提供了一个protected的方法:
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {return false;}
LinkedHashMap中的put方法直接继承HashMap的put方法,并没有重写,但是put方法中需要用到addEntry方法,并且LinkedHashMap对其进行了重写如下:
void addEntry(int hash, K key, V value, int bucketIndex) {super.addEntry(hash, key, value, bucketIndex);// Remove eldest entry if instructedEntry<K,V> eldest = header.after;if (removeEldestEntry(eldest)) {removeEntryForKey(eldest.key);}}
方法中调用了removeEldestEntry()方法,如果重写这个方法就可以实现LRU。
譬如:
package collections;import java.util.Map;public class LRUCache {private int capacity;private Map<Integer, Integer> cache;public LRUCache(final int capacity) {this.capacity = capacity;this.cache = new java.util.LinkedHashMap<Integer, Integer> (capacity, 0.75f, true) {// 定义put后的移除规则,大于容量就删除eldestprotected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {return size() > capacity;}};}public int get(int key) {if (cache.containsKey(key)) {return cache.get(key);} elsereturn -1;}public void set(int key, int value) {cache.put(key, value);}
}
引用《关于Java集合的小抄》来做一下总结:
扩展HashMap增加双向链表的实现,号称是最占内存的数据结构。支持iterator()时按Entry的插入顺序来排序(但是更新不算, 如果设置accessOrder属性为true,则所有读写访问都算)。
实现上是在Entry上再增加属性before/after指针,插入时把自己加到Header Entry的前面去。如果所有读写访问都要排序,还要把前后Entry的before/after拼接起来以在链表中删除掉自己。
参考资料:
- 《Java LinkedHashMap工作原理及实现》
- 《关于Java集合的小抄》
- 《Java程序优化》葛一鸣等编著
欢迎跳转到本文的原文链接:https://honeypps.com/java/java-collection-linkedhashmap/
欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。
Java集合框架:LinkedHashMap相关推荐
- Java集合框架综述,这篇让你吃透!
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:平凡希 cnblogs.com/xiaoxi/p/60899 ...
- Java集合框架的知识总结(1)
Java集合框架的知识总结(1) 所有集合类都位于java.util包下.集合中只能保存对象(保存对象的引用变量). Java的集合类主要由两个接口派生而出:Collection和Map,Collec ...
- java集合框架史上最详解(list set 以及map)
title: Java集合框架史上最详解(list set 以及map) tags: 集合框架 list set map 文章目录 一.集合框架总体架构 1.1 集合框架在被设计时需满足的目标 1.2 ...
- java集合框架综述
一.集合框架图 简化图: 说明:对于以上的框架图有如下几点说明 1.所有集合类都位于java.util包下.Java的集合类主要由两个接口派生而出:Collection和Map,Collection和 ...
- java集合框架容器 java框架层级 继承图结构 集合框架的抽象类 集合框架主要实现类...
本文关键词: java集合框架 框架设计理念 容器 继承层级结构 继承图 集合框架中的抽象类 主要的实现类 实现类特性 集合框架分类 集合框架并发包 并发实现类 什么是容器? 由一个或多个确 ...
- Java集合框架List,Map,Set等全面介绍
Java Collections Framework是Java提供的对集合进行定义,操作,和管理的包含一组接口,类的体系结构. Java集合框架的基本接口/类层次结构: java.util.Colle ...
- java集合框架图(一)
一.集合类简介 Java集合就像一种容器,可以把多个对象(实际上是对象的引用,但习惯上都称对象)"丢进"该容器中.从Java 5 增加了泛型以后,Java集合可以记住容器中对象的数 ...
- java集合框架(Framework)的性能
关于Java集合框架里面常用类的性能测试比较,包括(ArrayList/LinkedList /Vector/Queue/TreeSet/HashSet/LinkedHashSet/TreeMap/H ...
- 容器(一)剖析面试最常见问题之 Java 集合框架
转载自https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/Java%E9%9B%86%E5%90%88%E ...
- java list有序还是无序_牛批!2w字的Java集合框架面试题精华集(2020最新版),赶紧收藏。...
一个多月前,作者和一些小伙伴决定做一系列的 Java 知识点常见重要问题的小册,方便用来夯实基础!小册的标准就一个,那就是:取精华,取重点.每一本小册,我们都会充分关注我们所总结的知识点是否达到这个标 ...
最新文章
- 计算机鹅点云,CVPR 2020 | 用于点云中3D对象检测的图神经网络
- 推荐一本好书《改变,从阅读开始》
- PCL—关键点检测(rangeImage)低层次点云处理
- Matter-JS Composite.add 符合材料添加约束
- acer软件保护卡怎么解除_Acer软件保护卡下载
- Android GPS开发总结
- python 压缩 解压文件
- Sample larger than population or is negative
- 过度理想化与造梦周期
- 昆明市盘龙区打造铸牢中华民族共同体意识盘龙江示范带
- 政务系统信息网络安全的风险评估
- 近两年半导体重大收购兼并案
- 苹果电脑macos Ventura 13.0(22A380)dmg原版引导版镜像下载
- python字符映射表和字符替换
- 架构之——umi框架与dva的使用
- ffmpeg 分辨率 压缩_视频怎么在尽量不损害画质的前提下压缩?
- Maven工程中Pom.xml文件总是报依赖找不到
- e签宝认证服务API PHP请求签名鉴权生成
- Office365 自定义模板(恢复)
- java十六进制字符串和中文(字符串)互转以及转换乱码问题总结
热门文章
- oracle解析select,oracle_select语句例子解析
- matlab插值与拟合例题_菜鸟进阶系列:MATLAB数学建模·数据插值与拟合
- 不使用 Maven 等构建工具,而使用原始方法在 IntelliJ IDEA 中整合 Tomcat 部署 Web 应用
- 210122阶段三进程间信号
- 【图文详解】IDEA控制台运行时出现乱码:淇℃伅...
- Android Wifi方法大全
- 关于子网划分的几个捷径
- sp_updatestats和update statistics的区别
- CodeForces - 1481E Sorting Books(贪心+dp)
- HDU - 2049 不容易系列之(4)——考新郎(错排问题+组合数学)