标签:
前面分析了HashMap的实现,我们知道其底层数据存储是一个hash表(数组+单向链表)。接下来我们看一下另一个LinkedHashMap,它是HashMap的一个子类,他在HashMap的基础上维持了一个双向链表(hash表+双向链表),在遍历的时候可以使用插入顺序(先进先出,类似于FIFO),或者是最近最少使用(LRU)的顺序。
1
2
3
|
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> |
1
2
3
4
5
6
7
8
9
10
11
12
|
/** * The head of the doubly linked list. */ private transient Entry<K,V> header ; /** * The iteration ordering method for this linked hash map: <tt>true</tt> * for access -order, <tt> false</tt> for insertion -order. * * @serial */ private final boolean accessOrder; |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
/** * LinkedHashMap entry. */ private static class Entry<K,V> extends HashMap.Entry<K,V> { // These fields comprise the doubly linked list used for iteration. // 双向链表的上一个节点before和下一个节点after Entry<K,V> before, after ; // 构造方法直接调用父类HashMap的构造方法(super) Entry( int hash, K key, V value, HashMap.Entry<K,V> next) { super (hash, key, value, next); } /** * 从链表中删除当前节点的方法 */ private void remove() { // 改变当前节点前后两个节点的引用关系,当前节点没有被引用后,gc可以回收 // 将上一个节点的after指向下一个节点 before.after = after; // 将下一个节点的before指向前一个节点 after.before = before; } /** * 在指定的节点前加入一个节点到链表中(也就是加入到链表尾部) */ private void addBefore(Entry<K,V> existingEntry) { // 下面改变自己对前后的指向 // 将当前节点的after指向给定的节点(加入到existingEntry前面嘛) after = existingEntry; // 将当前节点的before指向给定节点的上一个节点 before = existingEntry.before ; // 下面改变前后最自己的指向 // 上一个节点的after指向自己 before.after = this ; // 下一个几点的before指向自己 after.before = this ; } // 当向Map中获取查询元素或修改元素(put相同key)的时候调用这个方法 void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; // 如果accessOrder为true,也就是使用最近较少使用顺序 if (lm.accessOrder ) { lm. modCount++; // 先删除,再添加,也就相当于移动了 // 删除当前元素 remove(); // 将当前元素加入到header前(也就是链表尾部) addBefore(lm. header); } } // 当从Map中删除元素的时候调动这个方法 void recordRemoval(HashMap<K,V> m) { remove(); } } |
可以看到Entry继承了HashMap中的Entry,但是LinkedHashMap中的Entry多了两个属性指向上一个节点的before和指向下一个节点的after,也正是这两个属性组成了一个双向链表。等等。。。Entry还有一个继承下来的next属性,这个next是单向链表中用来指向下一个节点的,怎么回事嘛,怎么又是单向链表又是双向链表呢,都要晕了对不对,其实想的没错,这里的节点即是Hash表中的单向链表中的一个节点,它又是LinkedHashMap维护的双向链表中的一个节点,是不是瞬间觉得高大上了。图解一下吧(不要告诉我图好乱,我看不懂。。。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
/** * 构造一个指定初始容量和加载因子的LinkedHashMap,默认accessOrder为false */ public LinkedHashMap( int initialCapacity, float loadFactor) { super (initialCapacity, loadFactor); accessOrder = false ; } /** * 构造一个指定初始容量的LinkedHashMap,默认accessOrder为false */ public LinkedHashMap( int initialCapacity) { super (initialCapacity); accessOrder = false ; } /** * 构造一个使用默认初始容量(16)和默认加载因子(0.75)的LinkedHashMap,默认accessOrder为false */ public LinkedHashMap() { super (); accessOrder = false ; } /** * 构造一个指定map的LinkedHashMap,所创建LinkedHashMap使用默认加载因子(0.75)和足以容纳指定map的初始容量,默认accessOrder为false 。 */ public LinkedHashMap(Map<? extends K, ? extends V> m) { super (m); accessOrder = false ; } /** * 构造一个指定初始容量、加载因子和accessOrder的LinkedHashMap */ public LinkedHashMap( int initialCapacity, float loadFactor, boolean accessOrder) { super (initialCapacity, loadFactor); this .accessOrder = accessOrder; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
/** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and load factor. * * @param initialCapacity the initial capacity * @param loadFactor the load factor * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ public HashMap( int initialCapacity, float loadFactor) { if (initialCapacity < 0 ) throw new IllegalArgumentException( "Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException( "Illegal load factor: " + loadFactor); // Find a power of 2 >= initialCapacity int capacity = 1 ; while (capacity < initialCapacity) capacity <<= 1 ; this .loadFactor = loadFactor; threshold = ( int )(capacity * loadFactor); table = new Entry[capacity]; init(); } /** * Initialization hook for subclasses. This method is called * in all constructors and pseudo -constructors (clone, readObject) * after HashMap has been initialized but before any entries have * been inserted. (In the absence of this method, readObject would * require explicit knowledge of subclasses.) */ void init() { |
哦,明白了,init()在HashMap中是一个空方法,也就是给子类留的一个回调函数,ok,我们来看下LinkedHashMap对init()方法的实现吧。
1
2
3
4
5
6
7
8
9
10
|
/** * Called by superclass constructors and pseudoconstructors (clone, * readObject) before any entries are inserted into the map. Initializes * the chain. */ void init() { // 初始化话header,将hash设置为-1,key、value、next设置为null header = new Entry<K,V>(- 1 , null , null , null ); // header的before和after都指向header自身 header.before = header. after = header ; |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
/** * This override alters behavior of superclass put method. It causes newly * allocated entry to get inserted at the end of the linked list and * removes the eldest entry if appropriate. */ void addEntry( int hash, K key, V value, int bucketIndex) { // 调用createEntry方法创建一个新的节点 createEntry(hash, key, value, bucketIndex); // Remove eldest entry if instructed, else grow capacity if appropriate // 取出header后的第一个节点(因为header不保存数据,所以取header后的第一个节点) Entry<K,V> eldest = header.after ; // 判断是容量不够了是要删除第一个节点还是需要扩容 if (removeEldestEntry(eldest)) { // 删除第一个节点(可实现FIFO、LRU策略的Cache) removeEntryForKey(eldest. key); } else { // 和HashMap一样进行扩容 if (size >= threshold) resize( 2 * table.length ); } } /** * This override differs from addEntry in that it doesn‘t resize the * table or remove the eldest entry. */ void createEntry( int hash, K key, V value, int bucketIndex) { // 下面三行代码的逻辑是,创建一个新节点放到单向链表的头部 // 取出数组bucketIndex位置的旧节点 HashMap.Entry<K,V> old = table[bucketIndex]; // 创建一个新的节点,并将next指向旧节点 Entry<K,V> e = new Entry<K,V>(hash, key, value, old); // 将新创建的节点放到数组的bucketIndex位置 table[bucketIndex] = e; // 维护双向链表,将新节点添加在双向链表header前面(链表尾部) e.addBefore( header); // 计数器size加1 size++; } /** * 默认返回false,也就是不会进行元素删除了。如果想实现cache功能,只需重写该方法 */ protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false ; } |
1
2
3
4
5
6
7
|
public V get(Object key) { Entry<K,V> e = (Entry<K,V>)getEntry(key); if (e == null ) return null ; e.recordAccess( this ); return e.value ; } |
7.是否包含
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/** * Returns <tt>true</tt> if this map maps one or more keys to the * specified value. * * @param value value whose presence in this map is to be tested * @return <tt> true</tt> if this map maps one or more keys to the * specified value */ public boolean containsValue(Object value) { // Overridden to take advantage of faster iterator // 遍历双向链表,查找指定的value if (value== null ) { for (Entry e = header .after; e != header; e = e.after ) if (e.value == null ) return true ; } else { for (Entry e = header .after; e != header; e = e.after ) if (value.equals(e.value )) return true ; } return false ; } |
标签:
原文地址:http://www.cnblogs.com/vn2019/p/5167733.html