一、概述
在总结了HashMap以后,现在来看看LikedHashMap的工作原理以及实现。首先还是先整一段LinkedHashMap程序:
LinkedHashMap<String,Integer> lmap = new LinkedHashMap<String,Integer>();
lmap.put("语文", 1);
lmap.put("数学", 2);
lmap.put("英语", 3);
lmap.put("历史", 4);
lmap.put("政治", 5);
lmap.put("地理", 6);
lmap.put("生物", 7);
lmap.put("化学", 8);
for(Entry<String, Integer> entry : lmap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
运行结果是:
语文: 1 数学: 2 英语: 3 历史: 4 政治: 5 地理: 6 生物: 7 化学: 8
这个结果和上一次的HashMap的运行结果不一样,LinkedHashMap的迭代输出结果保持了插入的顺序。是什么样的结构是得LinkedHashMap具有如此特性呢?让我们来看看LinkedHashMap的内部结构,有一个认识:
LinkedHashMap是Hash表和链表的实现,并且依靠双向链表保证了迭代顺序是插入的顺序。
二、三个重点实现的函数
在HashMap中提到下面的定义:
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }
LinkedHashMap继承于HashMap,因此也重新实现了这3个函数,顾名思义这三个函数的作用分别是:节点访问后、节点插入后、节点移除后做一些事情。
afterNodeAccess函数
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
// 如果定义了accessOrder,那么就保证最近访问节点放到最后
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
就是说在进行put之后就算是对节点的访问了,那么这个时候就会更新链表,把最近访问的放到最后,保证链表插入的有序性。
afterNodeInsertion函数
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
// 如果定义了溢出规则,则执行相应的溢出
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
afterNodeRemoval函数
void afterNodeRemoval(Node<K,V> e) { // unlink
// 从链表中移除节点
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.before = p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a == null)
tail = b;
else
a.before = b;
}
这个函数是在移除节点后调用的,就是将节点从双向链表中删除。
从上面的3个函数看出,基本上是为了保证双向链表中的节点次序或者双向链表容量所作的一些额外的事情,目的就是保持双向链表中节点的顺序要从eidest到youngest。
三、put和get函数
put函数在LinkedHashMap中没有重新实现,只是实现了afterNodeAccess和afterNodeInsertion两个回调函数。get函数则是重新实现并加入afterNodeAccess来保证访问顺序,下面是get函数的具体实现:
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
在accessOrder模式下,只要执行了get或者put操作的时候,就会产生structural modification,LinkedHashMap的其他操作基本上是为了维护具有访问顺序的双向链表。