描述

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

常用与关键方法

linkNodeLast方法

描述:

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

代码:

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

transferLinks方法

描述:

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

代码:

  1. private void transferLinks(LinkedHashMap.Entry<K,V> src,
  2. LinkedHashMap.Entry<K,V> dst) {
  3. // 同步before,同时保存到局部变量
  4. LinkedHashMap.Entry<K,V> b = dst.before = src.before;
  5. // 同步after,同时保存到局部变量
  6. LinkedHashMap.Entry<K,V> a = dst.after = src.after;
  7. // 检查before
  8. if (b == null)
  9. // 没有before.将dst设置为head节点
  10. head = dst;
  11. else
  12. // 有before,将before与dst关联
  13. b.after = dst;
  14. // 检查after
  15. if (a == null)
  16. // 没有after,将dst作为tail节点
  17. tail = dst;
  18. else
  19. // 有after,将after与dst连接
  20. a.before = dst;
  21. }

newNode方法

描述:

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

代码:

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

replacementNode方法

描述:

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

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

代码:

  1. Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
  2. LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
  3. LinkedHashMap.Entry<K,V> t =
  4. new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);
  5. // 替换节点
  6. transferLinks(q, t);
  7. return t;
  8. }

newTreeNode方法

描述:

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

代码:

  1. TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
  2. TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
  3. linkNodeLast(p);
  4. return p;
  5. }

replacementTreeNode方法

描述:

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

代码:

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

afterNodeRemoval方法

描述:

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

代码:

  1. void afterNodeRemoval(Node<K,V> e) { // unlink
  2. // b - before节点
  3. // p - 被删除节点
  4. // a - after节点
  5. LinkedHashMap.Entry<K,V> p =
  6. (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
  7. // 清除p的双端引用
  8. p.before = p.after = null;
  9. // 判断before是否存在
  10. if (b == null)
  11. // 没有before
  12. // 设置a为head
  13. head = a;
  14. else
  15. // 存在before
  16. // 连接b->a.注意,这是单向连接,现在还无法确认a是否存在.如果a为空,b就是链表中的唯一节点.after属性为null
  17. b.after = a;
  18. // 判断a是否为空
  19. if (a == null)
  20. // a为空
  21. // tail设置为b
  22. tail = b;
  23. else
  24. // a存在
  25. // 连接 a->b.注意,这里也是单向连接.如果b是空的话,a现在就是head且before属性是null
  26. a.before = b;
  27. }

afterNodeAccess方法

描述:

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

代码:

  1. void afterNodeAccess(Node<K,V> e) { // move node to last
  2. // oldTail.老尾部缓存
  3. LinkedHashMap.Entry<K,V> last;
  4. // 判断accessOrder.即按照访问(更新)顺序排列
  5. // 获取老尾部
  6. // 判断当前元素是不是尾部元素
  7. if (accessOrder && (last = tail) != e) {
  8. // accessOrder==true且e不要尾部元素
  9. // b - fefore
  10. // p - 当前元素
  11. // a - after
  12. LinkedHashMap.Entry<K,V> p =
  13. (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
  14. // 因为p将变为尾部元素,所以直接设置p.after为null.
  15. p.after = null;
  16. // 判断b
  17. if (b == null)
  18. // b为null,p节点就是head节点
  19. // a作为头部节点
  20. head = a;
  21. else
  22. // b不为空
  23. // 连接b->a. 注意,这里是单向连接.a可能为null,a.before的连接交给后续判断
  24. b.after = a;
  25. // 判断a
  26. if (a != null)
  27. // a不为空
  28. // a->b.注意,这里是单向链接.b可能是null.b.after的连接交给后续判断
  29. a.before = b;
  30. else
  31. // a为空.p节点就是tail节点
  32. // 这里有两个分支,需要判断b是否为空.此处a已经为空,如果b也为空,说明p是列表中的唯一节点.这个判断委托到后续判断中处理
  33. // 此时,last变量已经失去意义,它与p为同一对象.
  34. // 这里说一下赋值last = b;的作用.注意,这是本人猜测!
  35. // 是为了统一算法的外在样式.因为变量last在在本方法中是不会为空的,且在所有的情形中,都会调用p.before = last;last.after = p;进行连接(除了p是唯一元素的情况).
  36. // 那么在b存在的时候,再次与p进行连接,在链表结构上也是没有问题的,统一被视作被操作元素的前一个元素
  37. last = b;
  38. if (last == null)
  39. // p是唯一元素
  40. head = p;
  41. else {
  42. // 连接到尾部节点
  43. p.before = last;
  44. last.after = p;
  45. }
  46. // 更新尾部节点到p
  47. tail = p;
  48. // 修改计数++
  49. ++modCount;
  50. }
  51. }

内部类

LinkedHashIterator

描述:

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

代码:

  1. abstract class LinkedHashIterator {
  2. LinkedHashMap.Entry<K,V> next;
  3. LinkedHashMap.Entry<K,V> current;
  4. int expectedModCount;
  5. LinkedHashIterator() {
  6. // 初始化next节点为当前head
  7. next = head;
  8. expectedModCount = modCount;
  9. current = null;
  10. }
  11. public final boolean hasNext() {
  12. return next != null;
  13. }
  14. final LinkedHashMap.Entry<K,V> nextNode() {
  15. // 缓存next
  16. LinkedHashMap.Entry<K,V> e = next;
  17. // fast-fail
  18. if (modCount != expectedModCount)
  19. throw new ConcurrentModificationException();
  20. // next为空
  21. if (e == null)
  22. throw new NoSuchElementException();
  23. // 设置当前
  24. current = e;
  25. // 更新next到下一个
  26. next = e.after;
  27. return e;
  28. }
  29. public final void remove() {
  30. // 获取当前
  31. Node<K,V> p = current;
  32. // null判断
  33. if (p == null)
  34. throw new IllegalStateException();
  35. // fast-fail
  36. if (modCount != expectedModCount)
  37. throw new ConcurrentModificationException();
  38. // 迭代器置空
  39. current = null;
  40. // 获取key
  41. K key = p.key;
  42. // 调用父类的removeNode方法进行节点删除
  43. removeNode(hash(key), key, null, false, false);
  44. // 同步更新计数
  45. expectedModCount = modCount;
  46. }
  47. }

内部类

LinkedHashIterator实现

描述:

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

代码:

  1. final class LinkedKeyIterator extends LinkedHashIterator
  2. implements Iterator<K> {
  3. public final K next() { return nextNode().getKey(); }
  4. }
  5. final class LinkedValueIterator extends LinkedHashIterator
  6. implements Iterator<V> {
  7. public final V next() { return nextNode().value; }
  8. }
  9. final class LinkedEntryIterator extends LinkedHashIterator
  10. implements Iterator<Map.Entry<K,V>> {
  11. public final Map.Entry<K,V> next() { return nextNode(); }
  12. }

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. 大型情感剧集Selenium:8_selenium网页截图的四种方法

    有时候,有时候,你会相信一切有尽头-当你的代码走到了尽头,那么保留最后一刻的状态尤为重要,此时你该如何操作?记录日志-没有将浏览器当前的状态进行截图来的直观! 那么,selenium截取截屏,有哪些方 ...

  2. Snack3 一个新的微型JSON框架

    Snack3 一个新的微型JSON框架 一个作品,一般表达作者的一个想法.因为大家想法不同,所有作品会有区别.就做技术而言,因为有很多有区别的框架,所以大家可以选择的框架很丰富. snack3.基于j ...

  3. 遍历json数据的几种方式。

    json(JavaScript Object Notation),json是一种多用于存储和交换文本信息的语法.他能够进行数据的传输,通常和ajax一起使用.它具有体积小.速度快,易解析等诸多优点. ...

  4. python数据挖掘第二篇-爬虫

    python爬虫 urllib用法 eg1: from urllib import request data = request.urlopen(urlString).read() # data获取的 ...

  5. Json schema 以及在python中的jsonschema

    目录 1. JSON Schema简介 2. JSON Schema关键字详解 2.1 $schema 2.2 title和description 2.3 type 3 type常见取值 3.1 当t ...

  6. Java并发编程系列-(2) 线程的并发工具类

    2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了 ...

  7. 理解web服务器和数据库的负载均衡以及反向代理

    这里的“负载均衡”是指在网站建设中应该考虑的“负载均衡”.假设我们要搭建一个网站:aaa.me,我们使用的web服务器每秒能处理100条请求,而aaa.me这个网站最火的时候也只是每秒99条请求,那么 ...

  8. 20.DjangoRestFramework学习三之认证组件、权限组件、频率组件、url注册器、响应器、分页组件

    一 认证组件 1. 局部认证组件 我们知道,我们不管路由怎么写的,对应的视图类怎么写的,都会走到dispatch方法,进行分发, 在咱们看的APIView类中的dispatch方法的源码中,有个sel ...

  9. 【Spring MVC】Properties文件的加载

    [Spring MVC]Properties文件的加载 转载:https://www.cnblogs.com/yangchongxing/p/10726885.html 参考:https://java ...

  10. python学习-price

    """登录功能:用户名和密码存在{'name':'huahua','pwd':'123456'}字典中,通过控制台输入用户名和密码判读是否正确,然后给出对应的提示消息:登 ...