LinkedHashMap就这么简单【源码剖析】
前言
声明,本文用得是jdk1.8
前面已经讲了Collection的总览和剖析List集合以及散列表、Map集合、红黑树还有HashMap基础了:
本篇主要讲解LinkedHashMap~
看这篇文章之前最好是有点数据结构的基础:
当然了,如果讲得有错的地方还请大家多多包涵并不吝在评论去指正~
一、LinkedHashMap剖析
LinkedHashMap数据结构图:
ps:图片来源网络,侵删~
首先我们来看看类继承图:
我简单翻译了一下顶部的注释(我英文水平渣,如果有错的地方请多多包涵~欢迎在评论区下指正)
从顶部翻译我们就可以归纳总结出HashMap几点:
- 底层是散列表和双向链表
- 允许为null,不同步
- 插入的顺序是有序的(底层链表致使有序)
- 装载因子和初始容量对LinkedHashMap影响是很大的~
同时也给我带了几个疑问:
- access-ordered和insertion-ordered具体的使用和意思
- 为什么说初始容量对遍历没有影响?
希望可以在看源码的过程中可以解决掉我这两个疑问~那接下来就开始吧~
1.1LinkedHashMap的域
1.2LinkedHashMap重写的方法
下面我列举就这两个比较重要的:
这就印证了我们的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() {
// 默认是插入顺序
LinkedHashMap<Integer,String> insertOrder = 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<Integer> set = insertOrder.keySet();
for (Integer s : set) {
String mapValue = insertOrder.get(s);
System.out.println(s + "---" + mapValue);
}
}
测试一波:
接着,我们来测试一下以访问顺序来进行插入和遍历:
public static void accessOrder() {
// 设置为访问顺序的方式
LinkedHashMap<Integer,String> accessOrder = 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<Integer> sets = accessOrder.keySet();
for (Integer key : sets) {
String mapValue = accessOrder.get(key);
System.out.println(key + "---" + mapValue);
}
}
代码看似是没有问题,但是运行会出错的!
前面在看源码注释的时候我们就发现了:在AccessOrder的情况下,使用get方法也是结构性的修改!
为了简单看出他俩的区别,下面我就直接用key来进行看了~
以下是访问顺序的测试:
public static void accessOrder() {
// 设置为访问顺序的方式
LinkedHashMap<Integer,String> accessOrder = 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<Integer> sets = accessOrder.keySet();
for (Integer key : sets) {
System.out.println(key );
}
}
测试结果:
以下是插入顺序的测试(代码就不贴了,和上面几乎一样):
我们可以这样理解:最常用的将其放在链表的最后,不常用的放在链表的最前~
这个知识点以我的理解而言,它这个访问顺序在LinkedHashMap如果不重写用处并不大~它是用来给别的实现进行扩展的
- 因为最常被使用的元素再遍历的时候却放在了最后边,在LinkedHashMap中我也没找到对应的方法来进行调用~
- 一个
removeEldestEntry(Map.Entry<K,V> eldest)
方法,重写它可以删除最久未被使用的元素!! - 还有一个是
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<K,V> e)
这个方法
当然了,在remove的时候会涉及到上面重写的方法:
1.7遍历的方法
Set<Map.Entry<K,V>> entrySet()
是被重写的了
看到了这里,我们就知道为啥注释说:初始容量对遍历没有影响
因为它遍历的是LinkedHashMap内部维护的一个双向链表,而不是散列表(当然了,链表双向链表的元素都来源于散列表)
二、总结
LinkedHashMap比HashMap多了一个双向链表的维护,在数据结构而言它要复杂一些,阅读源码起来比较轻松一些,因为大多都由HashMap实现了..
阅读源码的时候我们会发现多态是无处不在的~子类用父类的方法,子类重写了父类的部分方法即可达到不一样的效果!
- 比如:LinkedHashMap并没有重写put方法,而put方法内部的
newNode()
方法重写了。LinkedHashMap调用父类的put方法,里面回调的是重写后的newNode()
,从而达到目的!
LinkedHashMap可以设置两种遍历顺序:
- 访问顺序(access-ordered)
- 插入顺序(insertion-ordered)
- 默认是插入顺序的
对于访问顺序,它是LRU(最近最少使用)算法的实现,要使用它要么重写LinkedListMap的几个方法(removeEldestEntry(Map.Entry<K,V> eldest)
和afterNodeInsertion(boolean evict)
),要么是扩展成LRUMap来使用,不然设置为访问顺序(access-ordered)的用处不大~
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,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友
LinkedHashMap就这么简单【源码剖析】的更多相关文章
- TreeMap就这么简单【源码剖析】
前言 声明,本文用得是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单[源码剖析] Map集合.散列表.红黑树介绍 HashMap就是这么简单[源码剖析] LinkedH ...
- 转:【Java集合源码剖析】LinkedHashmap源码剖析
转载请注明出处:http://blog.csdn.net/ns_code/article/details/37867985 前言:有网友建议分析下LinkedHashMap的源码,于是花了一晚上时 ...
- HashMap就是这么简单【源码剖析】
前言 声明,本文用得是jdk1.8 前面已经讲了Collection的总览和剖析List集合以及散列表.Map集合.红黑树的基础了: Collection总览 List集合就这么简单[源码剖析] Ma ...
- Animate.css动画库,简单的使用,以及源码剖析
animate.css是什么?能做些什么? animate.css是一个css动画库,使用它可以很方便的快捷的实现,我们想要的动画效果,而省去了操作js的麻烦.同时呢,它也是一个开源的库,在GitHu ...
- ConcurrentHashMap基于JDK1.8源码剖析
前言 声明,本文用的是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单[源码剖析] Map集合.散列表.红黑树介绍 HashMap就是这么简单[源码剖析] LinkedH ...
- 【java集合框架源码剖析系列】java源码剖析之HashSet
注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于HashSet的知识. 一HashSet的定义: public class HashSet&l ...
- jQuery之Deferred源码剖析
一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...
- Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现
声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...
- Apache Spark源码剖析
Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著 ISBN 978-7-121-25420- ...
随机推荐
- 解决新版的vue-cli后无法通过ip访问vue项目
今天遇到个问题,之前一直没遇到过,通过vue-cli脚手架搭建的项目,只能通过localhost访问,不能通过IP地址访问. 这是由 webpack 导致的,2017年11月12日的时候 webpac ...
- xlsx导入成--json
这两天遇到大难题了,就是这个 xlsx 导入问题,之前用的xlsx.full.min.js,写的导入,结果不兼容ie浏览器,研究这个也好长时间,网上居然还没有搜到合适的,自己写从xlsx官网上 ...
- VMware 下快速克隆出多个 Linux 环境
念念不忘,必有回响 好好工作,好好吃饭,困了倒头就睡:吃你认为好吃的,吃到饱:买贵的,你想买的:去玩去野: 就这样. 为什么要克隆多个 Linux 系统? 因为要玩阿. 其实也不是了,就是为了折腾嘛, ...
- Java NIO之套接字通道
1.简介 前面一篇文章讲了文件通道,本文继续来说说另一种类型的通道 -- 套接字通道.在展开说明之前,咱们先来聊聊套接字的由来.套接字即 socket,最早由伯克利大学的研究人员开发,所以经常被称为B ...
- STL --> stack栈
stack栈 c++stack(堆栈)是一个容器的改编,它实现了一个先进后出的数据结构(FILO),使用该容器时需要包含#include<stack>头文件: 定义stack对象示例: s ...
- Dynamics 365 for CRM:CRM与ADFS安装到同一台服务器,需修改ADFS服务端口号
CRM与ADFS安装到同一台服务器时,出现PluginRegistrationTool 及 CRM Outlook Client连接不上,需要修改ADFS的服务端口号,由默认的808修改为809: P ...
- VC++开发AutoCAD 2018/objectARX 用向导新建项目无法新建的问题
话说笔者最近想用新机子上装的AutoCAD ObjectARX 2018来进行二次开发,兴致勃勃安装了ARX API和向导, 然后打开VS2015,新建项目,无法新建. 折腾了一下,还是没有解决,后面 ...
- geotrellis使用(三十六)瓦片入库更新图层
前言 Geotrellis 是针对大数据量栅格数据进行分布式空间计算的框架,这一点毋庸置疑,并且无论采取何种操作,其实都是先将大块的数据切割成一定大小的小数据(专业术语为瓦片),这是分治的思想,也是分 ...
- 【Alpha】阶段总结报告
团队成员 陈家权 031502107 赖晓连 031502118 雷晶 031502119 林巧娜 031502125 庄加鑫 031502147 一.项目预期计划及现实进展 项目预期计划 现实进展 ...
- C第九次博客作业--指针
一.PTA实验作业 题目1:两个4位正整数的后两位互换 1. 本题PTA提交列 2. 设计思路 3.代码截图 本题调试过程碰到问题及PTA提交列表情况说明 刚开始想到的交换是令t=a;a=b;b=t这 ...