描述

  • 可以按照添加元素的顺序对元素进行迭代的HashMap的子类.
  • 注意,上面说的是加元素的顺序.也就是说,更新元素时,是不会影响遍历结构的的.除非设置参数accessOrdertrue,将更新元素放置到队末.
  • 这个类没有对其父类HashMap进行过多重写.主要通过实现afterNode*相关方法,在数据结构变更后,进行后置的链表结构更新进行维护.

常用与关键方法

linkNodeLast方法

描述:

  • 负责初始化成员变量headtail.
  • headtail初始化完成后,负责将目标元素p连接到tail并更新原有tail到目标元素p

代码:

private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
// 缓存尾部
LinkedHashMap.Entry<K,V> last = tail;
// 更新尾部到新元素
tail = p;
// 判断老尾部是否已经初始化
if (last == null)
// 老尾部为初始化,代表头部也没初始化.进行初始化操作
head = p;
else {
// 初始化以完成,将p链接到老尾部之后
p.before = last;
last.after = p;
}
}

transferLinks方法

描述:

使用dst替换src在双向链表中的位置

代码:

private void transferLinks(LinkedHashMap.Entry<K,V> src,
LinkedHashMap.Entry<K,V> dst) {
// 同步before,同时保存到局部变量
LinkedHashMap.Entry<K,V> b = dst.before = src.before;
// 同步after,同时保存到局部变量
LinkedHashMap.Entry<K,V> a = dst.after = src.after;
// 检查before
if (b == null)
// 没有before.将dst设置为head节点
head = dst;
else
// 有before,将before与dst关联
b.after = dst;
// 检查after
if (a == null)
// 没有after,将dst作为tail节点
tail = dst;
else
// 有after,将after与dst连接
a.before = dst;
}

newNode方法

描述:

重写了父类newNode方法.扩展双向链表的连接操作.返回了HashMap.Node的子类节点LinkedHashMap.Entry.

代码:

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
// 创建的新节点.直接链接到末端节点上
linkNodeLast(p);
return p;
}

replacementNode方法

描述:

扩展双向链表替换节点的操作.这个方法用于父类HashMapHashMap.TreeNode替换为HashMap.Node时调用,这里进行了重写,使用带有双向链表的LinkedHashMap.Entry作为返回值

注意: 这里HashMap.TreeNode是实现了LinkedHashMap.Entry的.也就是参数p,他可以直接强转为实现类LinkedHashMap.Entry

代码:

Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
LinkedHashMap.Entry<K,V> t =
new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);
// 替换节点
transferLinks(q, t);
return t;
}

newTreeNode方法

描述:

重写了父类方法newTreeNode.扩展双向链表的连接操作.同样,因为HashMap.TreeNode实现LinkedHashMap.Entry.可以直接通过linkNodeLast方法进行连接操作

代码:

TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
linkNodeLast(p);
return p;
}

replacementTreeNode方法

描述:

replacementNode.扩展双向链表替换节点的操作.只是节点类型变成了TreeNode.又因为他是LinkedHashMap.Entry的子类,可以直接交给transferLinks使用.进行双向链表替换操作

代码:

TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);
transferLinks(q, t);
return t;
}

afterNodeRemoval方法

描述:

删除节点后调用.进行双向链表同步

代码:

void afterNodeRemoval(Node<K,V> e) { // unlink
// b - before节点
// p - 被删除节点
// a - after节点
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
// 清除p的双端引用
p.before = p.after = null; // 判断before是否存在
if (b == null)
// 没有before
// 设置a为head
head = a;
else
// 存在before
// 连接b->a.注意,这是单向连接,现在还无法确认a是否存在.如果a为空,b就是链表中的唯一节点.after属性为null
b.after = a;
// 判断a是否为空
if (a == null)
// a为空
// tail设置为b
tail = b;
else
// a存在
// 连接 a->b.注意,这里也是单向连接.如果b是空的话,a现在就是head且before属性是null
a.before = b;
}

afterNodeAccess方法

描述:

更新节点后调用.进行双向链表同步

代码:

void afterNodeAccess(Node<K,V> e) { // move node to last
// oldTail.老尾部缓存
LinkedHashMap.Entry<K,V> last;
// 判断accessOrder.即按照访问(更新)顺序排列
// 获取老尾部
// 判断当前元素是不是尾部元素
if (accessOrder && (last = tail) != e) {
// accessOrder==true且e不要尾部元素 // b - fefore
// p - 当前元素
// a - after
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; // 因为p将变为尾部元素,所以直接设置p.after为null.
p.after = null; // 判断b
if (b == null)
// b为null,p节点就是head节点
// a作为头部节点
head = a;
else
// b不为空
// 连接b->a. 注意,这里是单向连接.a可能为null,a.before的连接交给后续判断
b.after = a; // 判断a
if (a != null)
// a不为空
// a->b.注意,这里是单向链接.b可能是null.b.after的连接交给后续判断
a.before = b;
else
// a为空.p节点就是tail节点
// 这里有两个分支,需要判断b是否为空.此处a已经为空,如果b也为空,说明p是列表中的唯一节点.这个判断委托到后续判断中处理
// 此时,last变量已经失去意义,它与p为同一对象.
// 这里说一下赋值last = b;的作用.注意,这是本人猜测!
// 是为了统一算法的外在样式.因为变量last在在本方法中是不会为空的,且在所有的情形中,都会调用p.before = last;last.after = p;进行连接(除了p是唯一元素的情况).
// 那么在b存在的时候,再次与p进行连接,在链表结构上也是没有问题的,统一被视作被操作元素的前一个元素
last = b;
if (last == null)
// p是唯一元素
head = p;
else {
// 连接到尾部节点
p.before = last;
last.after = p;
}
// 更新尾部节点到p
tail = p;
// 修改计数++
++modCount;
}
}

内部类

LinkedHashIterator

描述:

封装了针对链表结构的迭代器.并向子类提供了共有的扩展方法.

代码:

abstract class LinkedHashIterator {
LinkedHashMap.Entry<K,V> next;
LinkedHashMap.Entry<K,V> current;
int expectedModCount; LinkedHashIterator() {
// 初始化next节点为当前head
next = head;
expectedModCount = modCount;
current = null;
} public final boolean hasNext() {
return next != null;
} final LinkedHashMap.Entry<K,V> nextNode() {
// 缓存next
LinkedHashMap.Entry<K,V> e = next;
// fast-fail
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// next为空
if (e == null)
throw new NoSuchElementException();
// 设置当前
current = e;
// 更新next到下一个
next = e.after;
return e;
} public final void remove() {
// 获取当前
Node<K,V> p = current;
// null判断
if (p == null)
throw new IllegalStateException();
// fast-fail
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// 迭代器置空
current = null;
// 获取key
K key = p.key;
// 调用父类的removeNode方法进行节点删除
removeNode(hash(key), key, null, false, false);
// 同步更新计数
expectedModCount = modCount;
}
}

内部类

LinkedHashIterator实现

描述:

分别继承了LinkedHashIterator并使用前者的nextNode方法返回不同数据

代码:

final class LinkedKeyIterator extends LinkedHashIterator
implements Iterator<K> {
public final K next() { return nextNode().getKey(); }
} final class LinkedValueIterator extends LinkedHashIterator
implements Iterator<V> {
public final V next() { return nextNode().value; }
} final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}

LinkedHashMap源码学习的更多相关文章

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

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

  2. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

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

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

  4. hashMap源码学习记录

    hashMap作为java开发面试最常考的一个题目之一,有必要花时间去阅读源码,了解底层实现原理. 首先,让我们看看hashMap这个类有哪些属性 // hashMap初始数组容量 static fi ...

  5. linkedHashMap源码解析(JDK1.8)

    引言 关于java中的不常见模块,让我一下子想我也想不出来,所以我希望以后每次遇到的时候我就加一篇.上次有人建议我写全所有常用的Map,所以我研究了一晚上LinkedHashMap,把自己感悟到的解释 ...

  6. HashMap的源码学习以及性能分析

    HashMap的源码学习以及性能分析 一).Map接口的实现类 HashTable.HashMap.LinkedHashMap.TreeMap 二).HashMap和HashTable的区别 1).H ...

  7. JDK1.8源码学习-HashMap

    JDK1.8源码学习-HashMap 目录 一.HashMap简介 HashMap 主要用来存放键值对,它是基于哈希表的Map接口实现的,是常用的Java集合之一. 我们都知道在JDK1.8 之前 的 ...

  8. jQuery源码学习感想

    还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...

  9. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

随机推荐

  1. Spring Boot 最简单整合Shiro+JWT方式

    简介 目前RESTful大多都采用JWT来做授权校验,在Spring Boot 中可以采用Shiro和JWT来做简单的权限以及认证验证,在和Spring Boot集成的过程中碰到了不少坑.便结合自身以 ...

  2. #化鲲为鹏,我有话说# 鲲鹏弹性云服务器配置 Tomcat

    在鲲鹏弹性云服务器上配置 Tomcat 1 下载 Tomcat 地址 :http://tomcat.apache.org/ 根据需要选择版本,这里使用 Tomcat 9,下载压缩包 2 将文件发送到服 ...

  3. 硬核评测:企业上云的极速存储挑战,华为云全新极速IO云硬盘性能评测

    来源:至顶网 作者:董培欣 借助华为云全新一代极速IO云硬盘开启邀测的时机,至顶网评测实验室展开了一次华为云极速IO云硬盘与超高IO云硬盘的性能对比测试活动,并且尝试通过相关测试成绩,对云硬盘的应用能 ...

  4. 如何使用modelarts训练海量数据

    在modelarts上使用notebook上使用evs空间默认大小是5G,能满足大部分文本和图片训练模型的需求.如果训练数据稍微超过这个限额,可以适当的扩增下空间.但如果训练对象是视频,或是实际生成过 ...

  5. 第五章 初始jQuery

    jQuery与JavaScript: jQuery的用途: 访问和操作DOM元素: 控制页面样式: 对页面事件的处理: 方便地使用jQuery插件: 与Ajax技术的完美结合: jQuery的优势: ...

  6. 【Git】405- 分享:大牛总结的 Git 使用技巧

    作者:你喜欢吃青椒么 来源:juejin.im/post/5d157bf3f265da1bcc1954e6 前言 本文是参考廖雪峰老师的Git资料再加上我自己对Git的理解,记录我的Git学习历程,作 ...

  7. 【JS】341- 移动端滚动穿透的6种解决方案

    前言 相信能看到这篇文章的你,已经是遇到了这个问题.我就不gif展示问题效果了. 鉴于此问题是面试的常客,故特地针对滚动穿透这个疑难杂症,整理了六个解决方案. 各方法操作难易不同,分别针对弹层和bod ...

  8. windows系统的python开发环境的搭建

    step1:  访问python官方网站下载python安装包 https://www.python.org/downloads/windows/ 下载自己想要的版本 勾上Add Python XX ...

  9. CentOS6和CentOS7进入单用户模式重置root密码

    一.前言 如果在Linux系统下root密码丢失或者需要破解物理机器用户密码,可以通过进入系统单用户模式进行重置root密码.本文介绍CentOS6和CentOS7两个系统版本进行root密码重置. ...

  10. quick start guide for XMEGA ADC

    This is the quick start guide for the Analog to Digital Converter (ADC), with step-by-step instructi ...