2017-08-14 16:30:10

1、简介

LinkedHashMap继承自HashMap,能保证迭代顺序,支持其他Map可选的操作。采用双向链表存储元素,默认的迭代序是插入序。重复插入一个已经存在的key不影响此顺序。如果accessOrder参数被使用且置为true,迭代序使用访问序,访问序受put、get、putAll等方法的影响,但不受集合视图操作的影响(其实HashMap中好像并没有什么视图操作,不像List有subList方法)。LinkedHashMap不是线程安全的。

2、与HashMap相比特殊点

之前提到了,HashMap不保持顺序,但是LinkedHashMap能保证迭代序。同时还支持两种遍历顺序:插入序和访问序。之所以能实现这些功能和效果,是因为LinkedHashMap重载了LinkedEntry,实现了双向链表,所以插入时,同时插入到table数组和双向链表header,如果是访问序,则在get、putAll等方法操作时,会将操作过的元素链接到表尾,保证链表尾部永远是最近使用过元素。

总结就是:LinkedHashMap有两套元素存储机制:数组table和header,所有区别于HashMap的特点都是通过双向链表header实现的。

3、LinkedEntry

LinkedEntry继承自HashMapEntry,新增了2个元素:nxt和prv来实现双向链表,代码如下:

 /**
* LinkedEntry adds nxt/prv double-links to plain HashMapEntry.
*/
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;
}
}

4、put操作

LinkedHashMap没有重写put方法,而是重写了put方法调用的addNewEntry方法,该方法执行真正插入一个元素的操作。插入元素时,同时插入到table数组和header双向链表,代码如下:

 @Override void addNewEntry(K key, V value, int hash, int index) {
LinkedEntry<K, V> header = this.header; // 移除最久没使用过的元素,removeEldestEntry方法默认返回false,适合子类重写
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 table
// 1、将元素查到链表尾部;
// 2、将元素插入到table数组中;
LinkedEntry<K, V> oldTail = header.prv;
LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
key, value, hash, table[index], header, oldTail);//newTail.prv = oldTail, newTail.nxt = header,其实就是在Tail元素和Header元素之间插入
table[index] = oldTail.nxt = header.prv = newTail; //1、前一行代码只处理了部分双向链表插入操作,这里继续处理,oldTail.nxt = newTail, header.prv = newTail;
//2、插入table[index];
} @Override void addNewEntryForNullKey(V value) {
LinkedEntry<K, V> header = this.header; // 移除最久没使用过的元素,removeEldestEntry方法默认返回false,适合子类重写
LinkedEntry<K, V> eldest = header.nxt;
if (eldest != header && removeEldestEntry(eldest)) {
remove(eldest.key);
} //与addNewEntry方法类似,只是没有插入数组的操作
LinkedEntry<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;
}

5、get操作

     /**
* Relinks the given entry to the tail of the list. Under access ordering,
* this method is invoked whenever the value of a pre-existing entry is
* read by Map.get or modified by Map.put.
*/
private void makeTail(LinkedEntry<K, V> e) {
// 将元素e从当前位置移除
// Unlink e
e.prv.nxt = e.nxt;
e.nxt.prv = e.prv; // 连接到链表尾部
// Relink e as tail
LinkedEntry<K, V> header = this.header;
LinkedEntry<K, V> oldTail = header.prv;
e.nxt = header;
e.prv = oldTail;
oldTail.nxt = header.prv = e;
modCount++;
} @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;
} // Replace with Collections.secondaryHash when the VM is fast enough (http://b/8290590).
// 这里的遍历操作与HashMap的类似,唯一的区别是:如果是访问序,则将该元素移到链表尾部
int hash = 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;
}

6、preModify操作

在HashMap的put、putValueForNullKey方法用到了preModify方法,作用就是保证访问序,代码如下:

     @Override void preModify(HashMapEntry<K, V> e) {
if (accessOrder) {
makeTail((LinkedEntry<K, V>) e);
}
}

7、remove操作

LinkedHashMap方法没有重写remove方法,但是重写了postRemove方法,该方法在HashMap的remove方法中有调用,如下:

     //作用:从双向链表中移除元素e
@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)
}

8、Iterator体系

LinkedHashMap保证迭代顺序,支持插入序和访问序,那么它的Iterator是怎么实现的?体系与HashMap类似,实现了最顶层的LinkedHashIterator,如下:

 private abstract class LinkedHashIterator<T> implements Iterator<T> {
// 1、前面提到过,header只是一个虚拟元素,真正的表头元素是header.nxt,表尾元素是header.prv,所以遍历的时候从表头开始;
// 2、nextEntry很简单,直接取nxt即可;
LinkedEntry<K, V> next = header.nxt;
LinkedEntry<K, V> lastReturned = null;
int expectedModCount = modCount; public final boolean hasNext() {
return next != header;
} final LinkedEntry<K, V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
LinkedEntry<K, V> e = next;
if (e == header)
throw new NoSuchElementException();
next = e.nxt;
return lastReturned = e;
} // remove操作使用HashMap的remove方法,LinkedHashMap重写了postRemove方法
public final void remove() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (lastReturned == null)
throw new IllegalStateException();
LinkedHashMap.this.remove(lastReturned.key);
lastReturned = null;
expectedModCount = modCount;
}
}

至于其他的,就不用细说了,和HashMap类似。

【Java集合系列六】LinkedHashMap解析的更多相关文章

  1. Java集合系列[4]----LinkedHashMap源码分析

    这篇文章我们开始分析LinkedHashMap的源码,LinkedHashMap继承了HashMap,也就是说LinkedHashMap是在HashMap的基础上扩展而来的,因此在看LinkedHas ...

  2. 【Java集合系列】目录

    2017-07-29 13:49:40 一.Collection的全局继承关系 二.系列文章 [Java集合系列一]ArrayList解析 备注: 1.ArrayList本质上就是一个数组,所有对外提 ...

  3. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  4. Java 集合系列07之 Stack详细介绍(源码解析)和使用示例

    概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...

  5. Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...

  6. Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...

  7. Java 集合系列 09 HashMap详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  8. Java 集合系列 10 Hashtable详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  9. Java 集合系列 06 Stack详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

随机推荐

  1. Bootstrap3基础 引入本地的bootstrap

      内容 参数   OS   Windows 10 x64   browser   Firefox 65.0.2   framework     Bootstrap 3.3.7   editor    ...

  2. 20171017数据处理sql

    SELECT LEFT(RIGHT(进场时间,8),2), 车牌号,进场时间,支付时间 FROM 停车收费详情$;1是周日,7是周六SELECT COUNT(*),周几 FROM date_parts ...

  3. SonarQube 中文教程 (1)- 简介

    SonarQube是什么 SonarQube 是一个用于代码质量管理的开源平台,用于管理源代码的质量. 通过插件形式,可以支持包括 java, C#, C/C++, PL/SQL, Cobol, Ja ...

  4. VBoxManage安装

    扩展包的版本需要与VirtualBox的版本一致,通过帮助可以查看VirtualBox的版本信息,然后在http://download.virtualbox.org/virtualbox/寻找对应的版 ...

  5. zabbix服务器与客户端(Linux+Windows)的搭建

    zabbix监控搭建 一.搭建LNMP环境(MySQL) 创建数据库以及授权zabbix账户 mysql> create database zabbix character set utf8; ...

  6. MySQL单表多次查询和多表联合查询,哪个效率高?

    很多高性能的应用都会对关联查询进行分解. 简单地,可以对每个表进行一次单表查询,然后将结果在应用程序中进行关联.例如,下面这个查询: select * from tag join tag_post o ...

  7. VC++调试错误的解决方案

    这篇文章主要用来记录使用Visual Studio过程中,出现的各种error,并提供自己当时解决的方案.但是,一个error可能由不用原因引起的,文中案例仅供大家参考. nafxcwd.lib(th ...

  8. IntelliJ IDEA 和 Pycharm 破解

    关键网址:http://idea.lanyus.com/ 步骤: 1. 在http://idea.lanyus.com/上下载:JetbrainsCrack-2.9-release-enc.jar . ...

  9. Python自学:第三章 使用方法pop()删除元素

    motorcycle = ["honda", "yamaha", "suzuki"] last_owned = motorcycle.pop ...

  10. Write Sling Servlet using a resource type and selector

    @SlingServlet( resourceTypes = "geometrixx/components/homepage", selectors = "data&qu ...