一、最基本元素存储单元

 /**
* HashMap.Node subclass for normal LinkedHashMap entries.
*/
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
 还是原HashMap的,多了两个引用before,after,说明这是要用双向链表了

二、初始化 构造方法

public LinkedHashMap() {
super();
accessOrder = false;
}
多了 accessOrder = false; /**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
*
* @serial
*/
final boolean accessOrder;
accessOrder表示迭代顺序,true表示访问顺序,false表示插入顺序。

三、put方法

put方法没有重写,因此和HashMap是一样的,但也有不同,不同在于LinkedHashMap实现了afterNodeAccess,afterNodeInsertion方法
看HashMap put源码

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e); //这个方法HashMap是空实现,这里是发生hash冲突后,找到有相同key对值进行处理时调用
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict); //这个方法HashMap也是空实现,这里是完成新数据put后调用
return null;
}
LinkedHashMap具体实现 :
1.void afterNodeAccess(Node<K,V> e) { // move node to last 注释就说明了是把该元素移到最后
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) { //accessOrder用到了,默认false等于不运行,true时是按插入顺序
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
以上代码看出在按插入顺序时,在有相同key时,对当前节点将其移到链表末尾 2.void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
removeEldestEntry 方法在jdk1.8中固定返回false,因此不用关注此方法
说明一下removeElestEntry用于定义删除最老元素的规则。一旦需要删除最老节点,那么将会调用removeNode删除节点。
举个例子,如果一个链表只能维持100个元素,那么当插入了第101个元素时,以如下方式重写removeEldestEntry的话,那么将会删除最老的一个元素

 四、get方法(get进行了重写) ,在插入模式中,获取值后又一次进行把当前节点移到链表尾部操作

  有点类似LRU(最少最近使用)算法~

 /*基于访问排序*/
LinkedHashMap<String, String> linkedHashMap_accessOrder = new LinkedHashMap<>(10,0.75f,true);
linkedHashMap_accessOrder.put("name1", "josan1");
linkedHashMap_accessOrder.put("name2", "josan2");
linkedHashMap_accessOrder.put("name3", "josan3");
linkedHashMap_accessOrder.put("name4", "josan4");
linkedHashMap_accessOrder.put("name5", "josan5"); linkedHashMap_accessOrder.get("name5");
linkedHashMap_accessOrder.get("name1");
linkedHashMap_accessOrder.get("name2");
linkedHashMap_accessOrder.get("name4");
linkedHashMap_accessOrder.get("name3");
//51243顺序
for (Map.Entry<String, String> entry : linkedHashMap_accessOrder.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println("key:" + key + ",value:" + value);
}

get方法: (accessOrder 为true表示按照访问排序),

  public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}

将节点移动到最后:

 void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}

层层判断和更改引用之后,p已经独立出来了,就把p移到了map的末尾,实现了访问排序

总结

1. HashMap中TreeNode 即红黑树的节点类是继承LinkedHashMap.Entry

2. HashMap中没有使用双向链表,before, after没有使用,单纯的红黑树

3.LinkedHashMap是HashMap的子类,很多方法都是继承自父类,

LinkedHashMap中,存取等使用的也使用红黑树,但维护了before, after的链表属性,在存取时一样使用红黑树算法,

但keySet()、values()以及entrySet()等迭代时使用的是双向链表来进行的,这样的双向链表结构保证了插入顺序的有序。

4.总得来说,LinkedHashMap底层是数组加单项链表加双向链表。

5.数组加单向链表就是HashMap的结构,记录数据用,双向链表,存储插入顺序用。然后LInkedHashMap重写了两个方法,一个是添加entry,一个是创建entry,这两种时刻都要维护双向链表。

参考出处:https://www.cnblogs.com/zhaojj/p/7832680.html

LinkedHashMap笔记的更多相关文章

  1. jdk源码阅读笔记-LinkedHashMap

    Map是Java collection framework 中重要的组成部分,特别是HashMap是在我们在日常的开发的过程中使用的最多的一个集合.但是遗憾的是,存放在HashMap中元素都是无序的, ...

  2. java集合类学习笔记之LinkedHashMap

    1.简述 LinkedHashMap是HashMap的子类,他们最大的不同是,HashMap内部维护的是一个单向的链表数组,而LinkedHashMap内部维护的是一个双向的链表数组.HashMap是 ...

  3. JDK源码学习笔记——LinkedHashMap

    HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序. LinkedHashMap保证了元素迭代的顺序.该迭代顺序可以是插入顺序或者是访问顺序.通过维护一个 ...

  4. Java TreeMap 和 LinkedHashMap【笔记】

    Java TreeMap 和 LinkedHashMap[笔记] TreeMap TreeMap基本结构 TreeMap 底层的数据结构就是红黑树,和 HashMap 的红黑树结构一样 与HashMa ...

  5. LinkedHashMap源码阅读笔记(基于jdk1.8)

    LinkedHashMap是HashMap的子类,很多地方都是直接引用HashMap中的方法,所以需要注意的地方并不多.关键的点就是几个重写的方法: 1.Entry是继承与Node类,也就是Linke ...

  6. HashMap LinkedHashMap源码分析笔记

    MapClassDiagram

  7. 【Java学习笔记】HashMap子接口---LinkedHashMap

    特点: 存入元素的顺序   与   取出元素的顺序相同(与LinkedHashSet类似) import java.util.HashMap; import java.util.Iterator; i ...

  8. Java基础知识强化之集合框架笔记58:Map集合之LinkedHashMap类的概述

    1. LinkedHashMap类的概述 LinkedHashMap:Map接口的哈希表(保证唯一性) 和 链接(保证有序性)列表实现,具有可预知的迭代顺序. 2. 代码示例: package cn. ...

  9. Java笔记(八)TreeMap & TreeSet & LinkedHashMap

    TreeMap & TreeSet & LinkedHashMap 一.TreeMap HashMap缺陷:键值对之间没有特定的顺序.在TreeMap中, 键值对之间按键有序,Tree ...

随机推荐

  1. topjui.core.js

    var defaultConfig = { pageLoadComplete: false, config: { ctx: "", mainPage: false, pkName: ...

  2. Phpthink入门基础大全(CURD部分)

    [ad code=1 align=center] $data[1]['name'] = ‘阳光雨' $data[1]['email'] = 'sccscc@vip.qq.com' $User>a ...

  3. HDU 3607 线段树+DP+离散化

    题意:从左往右跳箱子,每个箱子有金币数量,只能从矮处向高处跳,求最大可获得金币数,数据规模1<=n<=1e5. 显然是一个dp的问题,不难得出dp[ i ] = max(dp[j] )+v ...

  4. 【POJ】1502 MPI Maelstrom

    题目链接:http://poj.org/problem?id=1502 题意:一个处理器给n-1个处理器发送广播,问最短时间.广播时并发,也就是各个路径就大的一方.输入如果是x的话说明两个处理器不能相 ...

  5. 【转】tomcat系统架构分析

    https://blog.csdn.net/wsl211511/article/details/51622991

  6. Centos7 pxe

    yum install dnsmasq mv /etc/dnsmasq.conf /etc/dnsmasq.conf.backup # vim /etc/dnsmasq.conf interface= ...

  7. Unity开发一些实用的提高效率的技巧

    该文章参考总结自Unity微信官方 原文: Unity小技巧介绍 1 如果编辑器意外崩溃了,但场景未保存,这时可以打开工程目录,找到/Temp/_Backupscenes/文件夹,可以看到有后缀名为. ...

  8. python库之lightgbm

    一.安装 https://blog.csdn.net/qq_40317897/article/details/81021958 参考文献: [1].LightGBM中文文档 https://light ...

  9. 利用 Dockerfile 定制镜像

    镜像的定制实际上就是定制每一层所添加的配置.文件. 如果我们可以把每一层修改.安装.构建.操作的命令都写入一个脚本,用这个脚本来构建.定制镜像, 那么之前提及的无法重复的问题.镜像构建透明性的问题.体 ...

  10. duilib教程之duilib入门简明教程15.自绘控件

    在[2013 duilib入门简明教程 -- 复杂控件介绍 (13)]中虽然介绍了界面设计器上的所有控件,但是还有一些控件并没有被放到界面设计器上,还有一些常用控件duilib并没有提供(比如菜单控件 ...