前面分析了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
;
}
|