摘要:习惯在微信看技术文章,想要获取更多的资源的同学,可以关注微信公众号。为了大家方便,刚新建了一下群,大家也可以去交流交流。谢谢支持了希望能多介绍给其他有需要的朋友
前言
声明,本文用得是jdk1.8
前面已经讲了Collection的总览和剖析List集合以及散列表、Map集合、红黑树还有HashMap基础了:
Collection总览
List集合就这么简单【源码剖析】
Map集合、散列表、红黑树介绍
HashMap就是这么简单【源码剖析】
本篇主要讲解LinkedHashMap~
看这篇文章之前最好是有点数据结构的基础:
Java实现单向链表
栈和队列就是这么简单
二叉树就这么简单
当然了,如果讲得有错的地方还请大家多多包涵并不吝在评论去指正~
一、LinkedHashMap剖析LinkedHashMap数据结构图:
ps:图片来源网络,侵删~
首先我们来看看类继承图:
我简单翻译了一下顶部的注释(我英文水平渣,如果有错的地方请多多包涵~欢迎在评论区下指正)
从顶部翻译我们就可以归纳总结出HashMap几点:
底层是散列表和双向链表
允许为null,不同步
插入的顺序是有序的(底层链表致使有序)
装载因子和初始容量对LinkedHashMap影响是很大的~
同时也给我带了几个疑问:
access-ordered和insertion-ordered具体的使用和意思
为什么说初始容量对遍历没有影响?
希望可以在看源码的过程中可以解决掉我这两个疑问~那接下来就开始吧~
下面我列举就这两个比较重要的:
这就印证了我们的LinkedHashMap底层确确实实是散列表和双向链表~
在构建新节点时,构建的是LinkedHashMap.Entry 不再是Node.
1.3构造方法可以发现,LinkedHashMap有5个构造方法:
下面我们来看看构造方法的定义是怎么样的:
从构造方法上我们可以知道的是:LinkedHashMap默认使用的是插入顺序
1.4put方法原本我是想要找put方法,看看是怎么实现的,后来没找着,就奇了个怪~
再顿了一下,原来LinkedHashMap和HashMap的put方法是一样的!LinkedHashMap继承着HashMap,LinkedHashMap没有重写HashMap的put方法
所以,LinkedHashMap的put方法和HashMap是一样的。
如果没看过HashMap就是这么简单【源码剖析】的同学,可进去看看~
当然了,在创建节点的时候,调用的是LinkedHashMap重写的方法~
1.5get方法get方法也是多了:判断是否为访问顺序~~~
讲到了这里,感觉我们可以简单测试一波了:
首先我们来看看已插入顺序来进行插入和遍历:
public static void insertOrder() { // 默认是插入顺序 LinkedHashMapinsertOrder = new LinkedHashMap(); String value = "关注公众号Java3y"; int i = 0; insertOrder.put(i++, value); insertOrder.put(i++, value); insertOrder.put(i++, value); insertOrder.put(i++, value); insertOrder.put(i++, value); //遍历 Set set = insertOrder.keySet(); for (Integer s : set) { String mapValue = insertOrder.get(s); System.out.println(s + "---" + mapValue); } }
测试一波:
接着,我们来测试一下以访问顺序来进行插入和遍历:
public static void accessOrder() { // 设置为访问顺序的方式 LinkedHashMapaccessOrder = new LinkedHashMap(16, 0.75f, true); String value = "关注公众号Java3y"; int i = 0; accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); // 遍历 Set sets = accessOrder.keySet(); for (Integer key : sets) { String mapValue = accessOrder.get(key); System.out.println(key + "---" + mapValue); } }
代码看似是没有问题,但是运行会出错的!
前面在看源码注释的时候我们就发现了:在AccessOrder的情况下,使用get方法也是结构性的修改!
为了简单看出他俩的区别,下面我就直接用key来进行看了~
以下是访问顺序的测试:
public static void accessOrder() { // 设置为访问顺序的方式 LinkedHashMapaccessOrder = new LinkedHashMap(16, 0.75f, true); String value = "关注公众号Java3y"; int i = 0; accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); // 访问一下key为3的元素再进行遍历 accessOrder.get(3); // 遍历 Set sets = accessOrder.keySet(); for (Integer key : sets) { System.out.println(key ); } }
测试结果:
以下是插入顺序的测试(代码就不贴了,和上面几乎一样):
我们可以这样理解:最常用的将其放在链表的最后,不常用的放在链表的最前~
这个知识点以我的理解而言,它这个访问顺序在LinkedHashMap如果不重写用处并不大~它是用来给别的实现进行扩展的
因为最常被使用的元素再遍历的时候却放在了最后边,在LinkedHashMap中我也没找到对应的方法来进行调用~
一个removeEldestEntry(Map.Entry
还有一个是afterNodeInsertion(boolean evict)方法,新增时判断是否需要删除最久未被使用的元素!!
去网上搜了几篇资料,都是讲LRUMap的实现的(也就是对LinkedHashMap进行扩展),有兴趣的同学可参考下列链接:
https://blog.csdn.net/exceptional_derek/article/details/11713255
http://www.php.cn/java-article-362041.html
https://www.jianshu.com/p/1a66529e1a2e
https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ%3D%3D&chksm=ebd639d5dca1b0c3ba5a26bd46d265544f4fdd468df6465e54d93da230c3457d4947e79eaf0c&idx=1&mid=2247485177&sn=93cfa2c2e6f3e5092e5850bdb5ea4cc3
1.6remove方法对于remove方法,在LinkedHashMap中也没有重写,它调用的还是父类的HashMap的remove()方法,在LinkedHashMap中重写的是:afterNodeRemoval(Node
当然了,在remove的时候会涉及到上面重写的方法:
1.7遍历的方法Set
看到了这里,我们就知道为啥注释说:初始容量对遍历没有影响
因为它遍历的是LinkedHashMap内部维护的一个双向链表,而不是散列表(当然了,链表双向链表的元素都来源于散列表)
二、总结LinkedHashMap比HashMap多了一个双向链表的维护,在数据结构而言它要复杂一些,阅读源码起来比较轻松一些,因为大多都由HashMap实现了..
阅读源码的时候我们会发现多态是无处不在的~子类用父类的方法,子类重写了父类的部分方法即可达到不一样的效果!
比如:LinkedHashMap并没有重写put方法,而put方法内部的newNode()方法重写了。LinkedHashMap调用父类的put方法,里面回调的是重写后的newNode(),从而达到目的!
LinkedHashMap可以设置两种遍历顺序:
访问顺序(access-ordered)
插入顺序(insertion-ordered)
默认是插入顺序的
对于访问顺序,它是LRU(最近最少使用)算法的实现,要使用它要么重写LinkedListMap的几个方法(removeEldestEntry(Map.Entry
LinkedHashMap遍历的是内部维护的双向链表,所以说初始容量对LinkedHashMap遍历是不受影响的
参考资料:
《Core Java》
https://blog.csdn.net/zxt0601/article/details/77429150
https://blog.csdn.net/panweiwei1994/article/details/76555359
https://zhuanlan.zhihu.com/p/28216267
https://blog.csdn.net/fan2012huan/article/details/51097331
https://www.cnblogs.com/chinajava/p/5808416.html
明天要是无意外的话,可能会写TreeMap,敬请期待哦~~~~
文章的目录导航:https://zhongfucheng.bitcron.com/post/shou-ji/wen-zhang-dao-hang
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/69024.html
摘要:下面总结一下集合常用的三个子类吧无序,允许为,底层是散列表红黑树,非线程同步有序,不允许为,底层是红黑树非线程同步迭代有序,允许为,底层是双向链表,非线程同步从结论而言我们就可以根据自己的实际情况来使用了。 前言 声明,本文用的是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单【源码剖析】 Map集合、散列表、红黑树介绍 HashMap就是这么简单【源码...
摘要:在这种情况下,是以其为根的树的最后一个结点。来源二总结底层是红黑树,能够实现该集合有序如果在构造方法中传递了对象,那么就会以对象的方法进行比较。 前言 声明,本文用得是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单【源码剖析】 Map集合、散列表、红黑树介绍 HashMap就是这么简单【源码剖析】 LinkedHashMap就这么简单【源码剖析】 本...
摘要:而在集合中,值仅仅是一个对象罢了该对象对本身而言是无用的。将这篇文章作为集合的总结篇,但觉得没什么好写就回答一些面试题去了,找了一会面试题又觉得不够系统。 前言 声明,本文用的是jdk1.8 花了一个星期,把Java容器核心的知识过了一遍,感觉集合已经无所畏惧了!!(哈哈哈....),现在来总结一下吧~~ 回顾目录: Collection总览 List集合就这么简单【源码剖析】 Ma...
摘要:下面我来简单总结一下的核心要点底层结构是散列表数组链表红黑树,这一点和是一样的。是将所有的方法进行同步,效率低下。而作为一个高并发的容器,它是通过部分锁定算法来进行实现线程安全的。 前言 声明,本文用的是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单【源码剖析】 Map集合、散列表、红黑树介绍 HashMap就是这么简单【源码剖析】 LinkedHas...
摘要:系统级线程核心级线程由操作系统内核进行管理。值得注意的是多线程的存在,不是提高程序的执行速度。实现多线程上面说了一大堆基础,理解完的话。虚拟机的启动是单线程的还是多线程的是多线程的。 前言 之前花了一个星期回顾了Java集合: Collection总览 List集合就这么简单【源码剖析】 Map集合、散列表、红黑树介绍 HashMap就是这么简单【源码剖析】 LinkedHashMa...
阅读 3432·2023-04-25 18:14
阅读 1525·2021-11-24 09:38
阅读 3243·2021-09-22 14:59
阅读 3059·2021-08-09 13:43
阅读 2561·2019-08-30 15:54
阅读 561·2019-08-30 13:06
阅读 1540·2019-08-30 12:52
阅读 2718·2019-08-30 11:13