前言

声明,本文用得是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进行扩展),有兴趣的同学可参考下列链接:

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遍历是不受影响的

参考资料:


明天要是无意外的话,可能会写TreeMap,敬请期待哦~~~~

文章的目录导航https://zhongfucheng.bitcron.com/post/shou-ji/wen-zhang-dao-hang

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友

LinkedHashMap就这么简单【源码剖析】的更多相关文章

  1. TreeMap就这么简单【源码剖析】

    前言 声明,本文用得是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单[源码剖析] Map集合.散列表.红黑树介绍 HashMap就是这么简单[源码剖析] LinkedH ...

  2. 转:【Java集合源码剖析】LinkedHashmap源码剖析

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/37867985   前言:有网友建议分析下LinkedHashMap的源码,于是花了一晚上时 ...

  3. HashMap就是这么简单【源码剖析】

    前言 声明,本文用得是jdk1.8 前面已经讲了Collection的总览和剖析List集合以及散列表.Map集合.红黑树的基础了: Collection总览 List集合就这么简单[源码剖析] Ma ...

  4. Animate.css动画库,简单的使用,以及源码剖析

    animate.css是什么?能做些什么? animate.css是一个css动画库,使用它可以很方便的快捷的实现,我们想要的动画效果,而省去了操作js的麻烦.同时呢,它也是一个开源的库,在GitHu ...

  5. ConcurrentHashMap基于JDK1.8源码剖析

    前言 声明,本文用的是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单[源码剖析] Map集合.散列表.红黑树介绍 HashMap就是这么简单[源码剖析] LinkedH ...

  6. 【java集合框架源码剖析系列】java源码剖析之HashSet

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于HashSet的知识. 一HashSet的定义: public class HashSet&l ...

  7. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  8. Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现

    声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...

  9. Apache Spark源码剖析

    Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著   ISBN 978-7-121-25420- ...

随机推荐

  1. 2016最热门的PHP框架

    每个PHP框架都拥有各自独特的地方.同时PHP语言已经获得了巨大的认同并且成为了世界上最通用的服务器脚本语言.PHP也俨然成为了最容易学习的web动态开发语言.在PHP发展的同时,PHP框架也迅速崛起 ...

  2. 详解小白利用eclipse+CDT+MinGW搭建C++开发环境

    安装JDK.安装Eclipse     2.打开Eclipse 找到“help”下的“Eclipse marketplace” 如图:    3.选择相应的C插件: 因为安装过所以显示installe ...

  3. Maven-12: 插件解析机制

    1. 插件仓库 2. 插件的默认groupId 3. 解析插件版本 4. 解析插件前缀

  4. 【Python】 获取MP3信息replica

    replica 初衷是想要整理iphone中的音乐.IOS(我自己的手机还是IOS8.3,新版本的系统可能有变化了)自带的音乐软件中所有音乐文件都存放在/var/mobile/Media/iTunes ...

  5. 关于bootstrap的form表单的输入框间距样式

    <!-- 模态弹出窗内容 --> <div class="modal" tabindex="-1" role="dialog&quo ...

  6. 数据库 --> 5种关系型数据库比较

    5种关系系数据库比较 目前,商品化的数据库管理系统以关系型数据库为主导产品,技术比较成熟.面向对象的数据库管理系统虽然技术先进,数据库易于开发.维护,但尚未有成熟的产品.国际国内的主导关系型数据库管理 ...

  7. linux --> 获取进程执行时间

    获取进程执行时间 一.时间概念 在linux下进行编程时,可能会涉及度量进程的执行时间.linux下进程的时间值分三种: 时钟时间(real time):指进程从开始执行到结束,实际执行的时间. 用户 ...

  8. java 单向链表实现

    1 class Node{//Node类 2 private String data; 3 private Node next; 4 public Node(String data){ 5 this. ...

  9. mysql的备份脚本

    mysql的备份脚本 脚本如下 #!/bin/sh # mysql_backup.sh: backup mysql databases and keep newest 5 days backup. # ...

  10. ssh框架-Struts2(一)

    Struts2 概述 用我们自己的话来说: struts是web层框架, 相当于Servlet 作用: 1. 获得请求参数 2. 调用业务 3. 分发转向 常用的WEB层框架 Struts2入门 1. ...