欢迎支持笔者新作:《深入理解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拼接起来以在链表中删除掉自己

参考资料:

  1. 《Java LinkedHashMap工作原理及实现》
  2. 《关于Java集合的小抄》
  3. 《Java程序优化》葛一鸣等编著

欢迎跳转到本文的原文链接:https://honeypps.com/java/java-collection-linkedhashmap/


欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。


Java集合框架:LinkedHashMap相关推荐

  1. Java集合框架综述,这篇让你吃透!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:平凡希 cnblogs.com/xiaoxi/p/60899 ...

  2. Java集合框架的知识总结(1)

    Java集合框架的知识总结(1) 所有集合类都位于java.util包下.集合中只能保存对象(保存对象的引用变量). Java的集合类主要由两个接口派生而出:Collection和Map,Collec ...

  3. java集合框架史上最详解(list set 以及map)

    title: Java集合框架史上最详解(list set 以及map) tags: 集合框架 list set map 文章目录 一.集合框架总体架构 1.1 集合框架在被设计时需满足的目标 1.2 ...

  4. java集合框架综述

    一.集合框架图 简化图: 说明:对于以上的框架图有如下几点说明 1.所有集合类都位于java.util包下.Java的集合类主要由两个接口派生而出:Collection和Map,Collection和 ...

  5. java集合框架容器 java框架层级 继承图结构 集合框架的抽象类 集合框架主要实现类...

    本文关键词: java集合框架  框架设计理念  容器 继承层级结构 继承图 集合框架中的抽象类  主要的实现类 实现类特性   集合框架分类 集合框架并发包 并发实现类 什么是容器? 由一个或多个确 ...

  6. Java集合框架List,Map,Set等全面介绍

    Java Collections Framework是Java提供的对集合进行定义,操作,和管理的包含一组接口,类的体系结构. Java集合框架的基本接口/类层次结构: java.util.Colle ...

  7. java集合框架图(一)

    一.集合类简介 Java集合就像一种容器,可以把多个对象(实际上是对象的引用,但习惯上都称对象)"丢进"该容器中.从Java 5 增加了泛型以后,Java集合可以记住容器中对象的数 ...

  8. java集合框架(Framework)的性能

    关于Java集合框架里面常用类的性能测试比较,包括(ArrayList/LinkedList /Vector/Queue/TreeSet/HashSet/LinkedHashSet/TreeMap/H ...

  9. 容器(一)剖析面试最常见问题之 Java 集合框架

    转载自https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/Java%E9%9B%86%E5%90%88%E ...

  10. java list有序还是无序_牛批!2w字的Java集合框架面试题精华集(2020最新版),赶紧收藏。...

    一个多月前,作者和一些小伙伴决定做一系列的 Java 知识点常见重要问题的小册,方便用来夯实基础!小册的标准就一个,那就是:取精华,取重点.每一本小册,我们都会充分关注我们所总结的知识点是否达到这个标 ...

最新文章

  1. 计算机鹅点云,CVPR 2020 | 用于点云中3D对象检测的图神经网络
  2. 推荐一本好书《改变,从阅读开始》
  3. PCL—关键点检测(rangeImage)低层次点云处理
  4. Matter-JS Composite.add 符合材料添加约束
  5. acer软件保护卡怎么解除_Acer软件保护卡下载
  6. Android GPS开发总结
  7. python 压缩 解压文件
  8. Sample larger than population or is negative
  9. 过度理想化与造梦周期
  10. 昆明市盘龙区打造铸牢中华民族共同体意识盘龙江示范带
  11. 政务系统信息网络安全的风险评估
  12. 近两年半导体重大收购兼并案
  13. 苹果电脑macos Ventura 13.0(22A380)dmg原版引导版镜像下载
  14. python字符映射表和字符替换
  15. 架构之——umi框架与dva的使用
  16. ffmpeg 分辨率 压缩_视频怎么在尽量不损害画质的前提下压缩?
  17. Maven工程中Pom.xml文件总是报依赖找不到
  18. e签宝认证服务API PHP请求签名鉴权生成
  19. Office365 自定义模板(恢复)
  20. java十六进制字符串和中文(字符串)互转以及转换乱码问题总结

热门文章

  1. oracle解析select,oracle_select语句例子解析
  2. matlab插值与拟合例题_菜鸟进阶系列:MATLAB数学建模·数据插值与拟合
  3. 不使用 Maven 等构建工具,而使用原始方法在 IntelliJ IDEA 中整合 Tomcat 部署 Web 应用
  4. 210122阶段三进程间信号
  5. 【图文详解】IDEA控制台运行时出现乱码:淇℃伅...
  6. Android Wifi方法大全
  7. 关于子网划分的几个捷径
  8. sp_updatestats和update statistics的区别
  9. CodeForces - 1481E Sorting Books(贪心+dp)
  10. HDU - 2049 不容易系列之(4)——考新郎(错排问题+组合数学)