类的定义

public class LinkedHashMap<K, V> extends HashMap<K, V> {}
  1. 基于双向链表实现,属于Map的一类,其父类是HashMap。最主要的是LinkedHashMap可以保证迭代顺序。如果既想要使用Map的功能又想要键值对插入和取出是有序的,可以使用LinkedHashMap
  2. 键和值支持任意类型,包括null
  3. 迭代的顺序就是键值对被插入Map的顺序。如果三个参数的构造方法中accessOrder参数被设置为true,那么键值对的迭代顺序就变为访问顺序(可以影响访问的操作是put get putAll),在Android的LRUCache中使用的数据结构就是LinkedHashMap。
  4. 非线程安全的,如果同时有多个线程执行改变Map结构的操作如put或者remove等操作的时候就需要调用者保证线程安全性
  5. 迭代器进行迭代的时候同样是不允许有修改Map结构的操作,否则抛出ConcurrentModificationException

主要成员变量

// true的时候按访问顺序排列键值对,false的时候按插入顺序
private final boolean accessOrder;
transient LinkedEntry<K, V> header;

构造函数

public LinkedHashMap() {init();accessOrder = false;
}public LinkedHashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);
}public LinkedHashMap(int initialCapacity, float loadFactor) {this(initialCapacity, loadFactor, false);
}public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {super(initialCapacity, loadFactor);init();this.accessOrder = accessOrder;
}public LinkedHashMap(Map<? extends K, ? extends V> map) {this(capacityForInitSize(map.size()));constructorPutAll(map);
}

构造函数和前面的集合类差不多,只是这里多了一个访问顺序的标志位

键值对实体

static class LinkedEntry<K, V> extends HashMapEntry<K, V> {LinkedEntry<K, V> nxt;LinkedEntry<K, V> prv;/** Create the header entry */LinkedEntry() {super(null, null, 0, null);nxt = prv = this;}/** Create a normal entry */LinkedEntry(K key, V value, int hash, HashMapEntry<K, V> next,LinkedEntry<K, V> nxt, LinkedEntry<K, V> prv) {super(key, value, hash, next);this.nxt = nxt;this.prv = prv;}
}

在HashMap键值对的实体上增加了nxt prv指针。除此之外在构造函数中调用了super();方法,该方法在HashMap中用于将映射实体加入到数组中。

返回最早加入的实体

public Entry<K, V> eldest() {LinkedEntry<K, V> eldest = header.nxt;return eldest != header ? eldest : null;
}

如果Map为空,那么返回NULL

添加新的映射实体

@Override void addNewEntry(K key, V value, int hash, int index) {LinkedEntry<K, V> header = this.header;// Remove eldest entry if instructed to do so.LinkedEntry<K, V> eldest = header.nxt;if (eldest != header && removeEldestEntry(eldest)) {remove(eldest.key);}// Create new entry, link it on to list, and put it into tableLinkedEntry<K, V> oldTail = header.prv;LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(key, value, hash, table[index], header, oldTail);table[index] = oldTail.nxt = header.prv = newTail;
}

这个方法在LruCache中会用到,每当添加一个新的实体的时候就删除最久的那个,是否删除通过removeEldestEntry(eldest)来判断,该方法返回true 或者false。方法的后半部分是一个双向链表的增加节点操作,头节点的nxt指向第一个节点,prv指向最后一个节点。添加新节点的时候主要是修改prv和prv所指向的节点的指针。

添加NULL key的映射实体

@Override void addNewEntryForNullKey(V value) {LinkedEntry<K, V> header = this.header;// Remove eldest entry if instructed to do so.LinkedEntry<K, V> eldest = header.nxt;if (eldest != header && removeEldestEntry(eldest)) {remove(eldest.key);}// Create new entry, link it on to list, and put it into tableLinkedEntry<K, V> oldTail = header.prv;LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(null, value, 0, null, header, oldTail);entryForNullKey = oldTail.nxt = header.prv = newTail;
}

前半部分和上面的添加操作一样,后面就是倒数第二句不同,没有设置key和hash,以及table[]

只添加不移除

@Override HashMapEntry<K, V> constructorNewEntry(K key, V value, int hash, HashMapEntry<K, V> next) {LinkedEntry<K, V> header = this.header;LinkedEntry<K, V> oldTail = header.prv;LinkedEntry<K, V> newTail= new LinkedEntry<K,V>(key, value, hash, next, header, oldTail);return oldTail.nxt = header.prv = newTail;
}

功能和上面的两个方法差不多,只没有判断是够进行移除操作

根据给定的key返回指定值

@Override public V get(Object key) {/** This method is overridden to eliminate the need for a polymorphic* invocation in superclass at the expense of code duplication.*/if (key == null) {HashMapEntry<K, V> e = entryForNullKey;if (e == null)return null;if (accessOrder)makeTail((LinkedEntry<K, V>) e);return e.value;}int hash = Collections.secondaryHash(key);HashMapEntry<K, V>[] tab = table;for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];e != null; e = e.next) {K eKey = e.key;if (eKey == key || (e.hash == hash && key.equals(eKey))) {if (accessOrder)makeTail((LinkedEntry<K, V>) e);return e.value;}}return null;
}private void makeTail(LinkedEntry<K, V> e) {// Unlink ee.prv.nxt = e.nxt;e.nxt.prv = e.prv;// Relink e as tailLinkedEntry<K, V> header = this.header;LinkedEntry<K, V> oldTail = header.prv;e.nxt = header;e.prv = oldTail;oldTail.nxt = header.prv = e;modCount++;
}

这部分代码和父类HashMap有些重复,但是优点是减少了由于多态导致的父类方法调用。代码中的entryForNullKey是父类HahMap中的成员变量,如果没有NULL key的映射,那么entryForNullKey也是NULL。

代码中使用的makeTail()的作用是将给定的映射实体从双向链表中取出,然后添加到链表尾部,在访问操作或者删除操作过程中,如果accessOrder为true,也就是需要按访问顺序进行排序的时候,都要调用MakeTail方法,将指定节点移动到尾部。

get()方法的后半部分看起来可能有一些奇怪,不是说LinkedHashMap是基于双向循环链表吗,为什么还有数组的迭代操作??实际上LinkedHashMap是数组和双向链表的结合,HashMap基于数组,LinkedHashMap是其子类,在HashMap的基础上增加了两个指针,除了将映射实体加入到数组中之外,还额外维护了两个指针。这样理解的话,get()方法的后半部分就比较好理解了,直接是按key的Hash值对数组进行遍历,如果找到对应的映射,则返回值,否则返回Null

删除指定节点

@Override void postRemove(HashMapEntry<K, V> e) {LinkedEntry<K, V> le = (LinkedEntry<K, V>) e;le.prv.nxt = le.nxt;le.nxt.prv = le.prv;le.nxt = le.prv = null; // Help the GC (for performance)
}

判断是否包含

@Override public boolean containsValue(Object value) {if (value == null) {for (LinkedEntry<K, V> header = this.header, e = header.nxt;e != header; e = e.nxt) {if (e.value == null) {return true;}}return false;}// value is non-nullfor (LinkedEntry<K, V> header = this.header, e = header.nxt;e != header; e = e.nxt) {if (value.equals(e.value)) {return true;}}return false;
}

由于LinkedHashMap继承于HashMap,支持null key 和null value,所以需要判断key是否为空。在父类HashMap中已经有了containsValue()方法了,这里的重载是基于链表的查找,这样可以减少迭代的开销。

清空集合

public void clear() {super.clear();// Clear all links to help GCLinkedEntry<K, V> header = this.header;for (LinkedEntry<K, V> e = header.nxt; e != header; ) {LinkedEntry<K, V> nxt = e.nxt;e.nxt = e.prv = null;e = nxt;}header.nxt = header.prv = header;
}

清空操作不仅仅是清空链表间的指针链,还需要调用父类的clear()清空映射实体

【Java源码分析】LinkedHashMap源码分析相关推荐

  1. Java类集框架 —— LinkedHashMap源码分析

    前言 我们知道HashMap底层是采用数组+单向线性链表/红黑树来实现的,HashMap在扩容或者链表与红黑树转换过程时可能会改变元素的位置和顺序.如果需要保存元素存入或访问的先后顺序,那就需要采用L ...

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

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

  3. LinkedHashMap源码剖析

    1. LinkedHashMap简介 LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加入了一个双向链表的头结点,将所有put到LinkedHashmap的节 ...

  4. LinkedHashMap 源码详细分析(JDK1.8)

    1. 概述 LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题.除此之外,Linke ...

  5. 深度分析Java的ClassLoader机制(源码级别)

    转载自 深度分析Java的ClassLoader机制(源码级别) Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取 ...

  6. 由一次线上故障来理解下 TCP 三握、四挥 Java 堆栈分析到源码的探秘

    本文导读: 生产故障场景介绍 TCP 建连三次握手过程 TCP 断连四次挥手过程 结合 Java 堆栈剖析源码 再从堆栈中找到"罪魁祸首" 问题优化方案总结 1.生产故障场景介绍 ...

  7. Java设计模式学习以及底层源码分析

    源码在分支master 工厂模式 把具体创建产品的细节封装起来,你要什么产品,我给你什么产品即可. 简单工厂模式 工厂方法模式 缓存层:抽象类 抽象工厂模式 缓存层是:接口 原型模式 问题: 原型模式 ...

  8. Java集合Collection源码系列-ArrayList源码分析

    Java集合系列-ArrayList源码分析 文章目录 Java集合系列-ArrayList源码分析 前言 一.为什么想去分析ArrayList源码? 二.源码分析 1.宏观上分析List 2.方法汇 ...

  9. java计算机毕业设计营养分析系统源码+数据库+系统+lw文档+部署

    java计算机毕业设计营养分析系统源码+数据库+系统+lw文档+部署 java计算机毕业设计营养分析系统源码+数据库+系统+lw文档+部署 本源码技术栈: 项目架构:B/S架构 开发语言:Java语言 ...

  10. 基于Java毕业设计成绩分析系统源码+系统+mysql+lw文档+部署软件

    基于Java毕业设计成绩分析系统源码+系统+mysql+lw文档+部署软件 基于Java毕业设计成绩分析系统源码+系统+mysql+lw文档+部署软件 本源码技术栈: 项目架构:B/S架构 开发语言: ...

最新文章

  1. 甲小姐对话稚晖君:深度学习并非AI的终点
  2. 华为上半年手机销量_十月京东手机销量!华为mate40火爆,苹果11近百万销量
  3. 嵌入式linux编译环境搭建,嵌入式Linux之旅——环境搭建篇之交叉编译工具的安装...
  4. 使用牛刀云开发微信小程序(问题集锦)
  5. 粉色温馨——HTML框架示例
  6. 消息队列_消息队列:kafka
  7. 宝塔linux忘记密码,宝塔忘记登录入口了怎么解决 宝塔面板密码忘记了怎么办
  8. 内网ip 设置_我的天,大牛黑客轻而易举打穿三层内网,吃惊
  9. python实战1.0——爬取知乎某问题下的回复
  10. 1月29日云栖精选夜读 | 拿下两个世界第一,阿里人机对话模型成人工智能国际通用标准...
  11. 关于Jquery.Data()和HTML标签的data-*属性
  12. 域名解析信息易语言代码
  13. Excel操作技巧大全
  14. 基于FreeMarker+aspose的Word模板制作及打印
  15. Html+Css 3D旋转立方体
  16. Window笔记本触摸板手势大全
  17. CSS拓展选择器 组合选择器 后代选择器 交集选择器 伪类选择器
  18. 「游戏」岩浆逃脱2.1
  19. ATL SERVER
  20. 绿荫工作室爱选修app内测

热门文章

  1. 翻译java_翻译示例代码
  2. 12.QT线程的两种启动方式
  3. Win7和Win10安装VC6.0注意事项
  4. c语言复杂性,C语言复杂函数
  5. python 列表表达式 if_python中if else如何判断表达式成立?
  6. MySQL原生密码认证
  7. 机器学习开发者的现代化路径:不需要从统计学微积分开始
  8. android控件的对齐方式
  9. 基于特征的推荐算法【转】
  10. JavaScript引擎研究与C、C++与互调用(转)