【Java集合系列六】LinkedHashMap解析

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来实现双向链表,代码如下:

 1 /**
 2      * LinkedEntry adds nxt/prv double-links to plain HashMapEntry.
 3      */
 4     static class LinkedEntry<K, V> extends HashMapEntry<K, V> {
 5         LinkedEntry<K, V> nxt;
 6         LinkedEntry<K, V> prv;
 7
 8         /** Create the header entry */
 9         LinkedEntry() {
10             super(null, null, 0, null);
11             nxt = prv = this;
12         }
13
14         /** Create a normal entry */
15         LinkedEntry(K key, V value, int hash, HashMapEntry<K, V> next,
16                     LinkedEntry<K, V> nxt, LinkedEntry<K, V> prv) {
17             super(key, value, hash, next);
18             this.nxt = nxt;
19             this.prv = prv;
20         }
21     }

4、put操作

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

 1 @Override void addNewEntry(K key, V value, int hash, int index) {
 2         LinkedEntry<K, V> header = this.header;
 3
 4         // 移除最久没使用过的元素,removeEldestEntry方法默认返回false,适合子类重写
 5         LinkedEntry<K, V> eldest = header.nxt;
 6         if (eldest != header && removeEldestEntry(eldest)) {
 7             remove(eldest.key);
 8         }
 9
10         // Create new entry, link it on to list, and put it into table
11         // 1、将元素查到链表尾部;
12         // 2、将元素插入到table数组中;
13         LinkedEntry<K, V> oldTail = header.prv;
14         LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
15                 key, value, hash, table[index], header, oldTail);//newTail.prv = oldTail, newTail.nxt = header,其实就是在Tail元素和Header元素之间插入
16         table[index] = oldTail.nxt = header.prv = newTail; //1、前一行代码只处理了部分双向链表插入操作,这里继续处理,oldTail.nxt = newTail, header.prv = newTail;
17                                                            //2、插入table[index];
18     }
19
20     @Override void addNewEntryForNullKey(V value) {
21         LinkedEntry<K, V> header = this.header;
22
23         // 移除最久没使用过的元素,removeEldestEntry方法默认返回false,适合子类重写
24         LinkedEntry<K, V> eldest = header.nxt;
25         if (eldest != header && removeEldestEntry(eldest)) {
26             remove(eldest.key);
27         }
28
29         //与addNewEntry方法类似,只是没有插入数组的操作
30         LinkedEntry<K, V> oldTail = header.prv;
31         LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
32                 null, value, 0, null, header, oldTail);
33         entryForNullKey = oldTail.nxt = header.prv = newTail;
34     }

5、get操作

 1     /**
 2      * Relinks the given entry to the tail of the list. Under access ordering,
 3      * this method is invoked whenever the value of a  pre-existing entry is
 4      * read by Map.get or modified by Map.put.
 5      */
 6     private void makeTail(LinkedEntry<K, V> e) {
 7         // 将元素e从当前位置移除
 8         // Unlink e
 9         e.prv.nxt = e.nxt;
10         e.nxt.prv = e.prv;
11
12         // 连接到链表尾部
13         // Relink e as tail
14         LinkedEntry<K, V> header = this.header;
15         LinkedEntry<K, V> oldTail = header.prv;
16         e.nxt = header;
17         e.prv = oldTail;
18         oldTail.nxt = header.prv = e;
19         modCount++;
20     }
21
22     @Override public V get(Object key) {
23         /*
24          * This method is overridden to eliminate the need for a polymorphic
25          * invocation in superclass at the expense of code duplication.
26          */
27         if (key == null) {
28             HashMapEntry<K, V> e = entryForNullKey;
29             if (e == null)
30                 return null;
31             if (accessOrder) //如果是访问序,将当前元素移到链表尾部(保证最近使用的元素在尾部)
32                 makeTail((LinkedEntry<K, V>) e);
33             return e.value;
34         }
35
36         // Replace with Collections.secondaryHash when the VM is fast enough (http://b/8290590).
37         // 这里的遍历操作与HashMap的类似,唯一的区别是:如果是访问序,则将该元素移到链表尾部
38         int hash = secondaryHash(key);
39         HashMapEntry<K, V>[] tab = table;
40         for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
41              e != null; e = e.next) {
42             K eKey = e.key;
43             if (eKey == key || (e.hash == hash && key.equals(eKey))) {
44                 if (accessOrder)
45                     makeTail((LinkedEntry<K, V>) e);
46                 return e.value;
47             }
48         }
49         return null;
50     }

6、preModify操作

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

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

7、remove操作

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

1     //作用:从双向链表中移除元素e
2     @Override void postRemove(HashMapEntry<K, V> e) {
3         LinkedEntry<K, V> le = (LinkedEntry<K, V>) e;
4         le.prv.nxt = le.nxt;
5         le.nxt.prv = le.prv;
6         le.nxt = le.prv = null; // Help the GC (for performance)
7     }

8、Iterator体系

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

 1 private abstract class LinkedHashIterator<T> implements Iterator<T> {
 2         // 1、前面提到过,header只是一个虚拟元素,真正的表头元素是header.nxt,表尾元素是header.prv,所以遍历的时候从表头开始;
 3         // 2、nextEntry很简单,直接取nxt即可;
 4         LinkedEntry<K, V> next = header.nxt;
 5         LinkedEntry<K, V> lastReturned = null;
 6         int expectedModCount = modCount;
 7
 8         public final boolean hasNext() {
 9             return next != header;
10         }
11
12         final LinkedEntry<K, V> nextEntry() {
13             if (modCount != expectedModCount)
14                 throw new ConcurrentModificationException();
15             LinkedEntry<K, V> e = next;
16             if (e == header)
17                 throw new NoSuchElementException();
18             next = e.nxt;
19             return lastReturned = e;
20         }
21
22         // remove操作使用HashMap的remove方法,LinkedHashMap重写了postRemove方法
23         public final void remove() {
24             if (modCount != expectedModCount)
25                 throw new ConcurrentModificationException();
26             if (lastReturned == null)
27                 throw new IllegalStateException();
28             LinkedHashMap.this.remove(lastReturned.key);
29             lastReturned = null;
30             expectedModCount = modCount;
31         }
32     }

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

时间: 2024-10-16 15:59:49

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

Java 集合系列16之 HashSet详细介绍(源码解析)和使用示例

概要 这一章,我们对HashSet进行学习.我们先对HashSet有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashSet.内容包括:第1部分 HashSet介绍第2部分 HashSet数据结构第3部分 HashSet源码解析(基于JDK1.6.0_45)第4部分 HashSet遍历方式第5部分 HashSet示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3311252.html 第1部分 HashSet介绍 HashSet 简

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

java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 概要  和学习ArrayList一样,接下来呢,我们先对LinkedList有个整体认识,然后再学习它的源码:最后再通过实例来学会使用LinkedList.内容包括:第1部分 LinkedList介绍第2部分 LinkedList数

Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例

java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 概要 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解ArrayList.先对ArrayLis

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

java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 Java 集合系列 05 Vector详细介绍(源码解析)和使用示例 Java 集合系列 06 Stack详细介绍(源码解析)和使用示例 第1部分 Stack介绍 Stack简介 Stack是栈.它的特性是:先进后出(FILO, F

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

概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Hashtable介绍第2部分 Hashtable数据结构第3部分 Hashtable源码解析(基于JDK1.6.0_45)第4部分 Hashtable遍历方式第5部分 Hashtable示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3310887.h

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

概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它.内容包括:第1部分 Stack介绍第2部分 Stack源码解析(基于JDK1.6.0_45)第3部分 Vector示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3308852.html 第1部分 Stack介绍 Stack简介 Stack是栈.它的

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

此页面为WP8"Surface Pro 3"应用的发布页面. "Surface Pro 3"是一款收集Surface Pro 3的玩机技巧的WP8程序,更好的帮助Surface用户理解并使用它. 此页面主要记录开发进度.APP发布等情况. -------------------相关进度--------------------- 目前进度:UI相关资源前期准备中,各相关开放平台的AppID申请中... Java 集合系列 09 HashMap详细介绍(源码解析)和使用

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

概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它.内容包括:第1部分 Stack介绍第2部分 Stack源码解析(基于JDK1.6.0_45)第3部分 Vector示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3308852.html 第1部分 Stack介绍 Stack简介 Stack是栈.它的

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

概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对LinkedList有个整体认识,然后再学习它的源码:最后再通过实例来学会使用LinkedList.内容包括:第1部分 LinkedList介绍第2部分 LinkedList数据结构第3部分 LinkedList源码解析(基于JDK1.6.0_45)第4部分 LinkedList遍历方式第5部分 LinkedL