LinkedHashMap 原理

基于jdk1.8

HashMap原理:http://www.cnblogs.com/zhaojj/p/7805376.html

LinkedHashMap 继承HashMap
没有重写put resize等方法 因此基本数据结构是相同的数组、链表、红黑树

说说不同:
一、最基本元素存储单元

static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
还是原HashMap的,多了两个引用before,after,说明这是要用双向链表了

二、初始化 构造方法

public LinkedHashMap() {
super();
accessOrder = false;
}
多了 accessOrder = false; /**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
*
* @serial
*/
final boolean accessOrder;
accessOrder表示迭代顺序,true表示访问顺序,false表示插入顺序。 后面用到再具体分析

三、put方法

put方法没有重写,因此和HashMap是一样的,但也有不同,不同在于LinkedHashMap实现了afterNodeAccess,afterNodeInsertion方法
看HashMap put源码

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e); //这个方法HashMap是空实现,这里是发生hash冲突后,找到有相同key对值进行处理时调用
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict); //这个方法HashMap也是空实现,这里是完成新数据put后调用
return null;
} LinkedHashMap具体实现
1.void afterNodeAccess(Node<K,V> e) { // move node to last 注释就说明了是把该元素移到最后
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) { //accessOrder用到了,默认false等于不运行,true时是按插入顺序
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;
}
}
以上代码看出在按插入顺序时,在有相同key时,对当前节点将其移到链表末尾 2.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);
}
}
removeEldestEntry 方法在jdk1.8中固定返回false,因此不用关注此方法
说明一下removeElestEntry用于定义删除最老元素的规则。一旦需要删除最老节点,那么将会调用removeNode删除节点。 举个例子,如果一个链表只能维持100个元素,那么当插入了第101个元素时,以如下方式重写removeEldestEntry的话,那么将会删除最老的一个元素

四、get方法
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;
} 唯一需要关注的就是在插入模式中,获取值后又一次进行把当前节点移到链表尾部操作

五、其它
1.需要注意HashMap中TreeNode 即红黑树的节点类是继承LinkedHashMap.Entry
在HashMap中没有使用双向链表,before, after没有使用,单纯的红黑树
在LinkedHashMap中,存取等使用的也使用红黑树,但维护了before, after的链表属性,在存取时一样使用红黑树算法,但keySet()、values()以及entrySet()等迭代时使用的是双向链表来进行的

即存取是一样 但遍历不同,LinkedHashMap把所有插入的元素又全部重新维护了一个双向链表遍历时用的是这个链表,它的有序就是靠这个链表了玩的。简单理解就是和hashmap都一样,只是加了个双向链表的结构用于遍历体现有序性。

2.LinkedHashMap只有在使用三个参数的构造方法并制定accessOrder为true时,才有顺序,不指定那么和HashMap基本没什么大的区别
所谓的顺序是指插入顺序,而不是通常意义上的大小顺序

05-23 22:03