从之前的LinkedList源码分析来看,带有Linked的,其实就是和双链表相关,毫无疑问,LinkedHashMap就是HashMap再多加一个双向链表,其内部的存储规则和HashMap是一样的,但是在迭代中,HashMap是无序的,LinkedHashMap是有序的
LinkedHashMap维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
LinkedHashMap继承自HashMap,因此继承了HashMap的成员变量等
//header是双链表的头结点
private transient Entry header;
//accessOrder为true时,按访问顺序排序,false时,按插入顺序排序
private final boolean accessOrder;
LinkedHashMap中有两个特有的成员变量 — header和accessOrder,接下来会讲到
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
LinkedHashMap共有五个构造方法,每一个方法都是调用父类HashMap的构造方法,并且accessOrder都为false,也就是默认按插入顺序排序
图中,有两个比较特殊的变量,after和before,他们是Entry引用,before指向当前节点的前一个节点,after指向当前节点的后一个节点(注意绿线)
LinkedHashMap中,覆盖了Entry内部类,在原来的Entry的实现上,增加了before和after,这就是双联链表的前引用和后引用,并增加了addBefore和remove等重要方法
private static class Entry<K,V> extends HashMap.Entry<K,V> {
// These fields comprise the doubly linked list used for iteration.
Entry before, after;
Entry(int hash, K key, V value, HashMap.Entry next) {
super(hash, key, value, next);
}
/**
* Removes this entry from the linked list.
*/
private void remove() {
before.after = after;
after.before = before;
}
/**
* Inserts this entry before the specified existing entry in the list.
*/
private void addBefore(Entry existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
在HashMap中,存储实现的流程是这样的:
put() -> addEntry() -> createEntry()
put()中计算当前key的hash值所在的哈希表的索引位置,并在createEntry()中完成key-value节点在该索引位置的插入
而在LinkedHashMap中,覆盖了addEntry() 和 createEntry()方法的实现,而没有覆盖put方法,因此这就证明了LinkedHashMap的存储规则是和HashMap一样的
我们来看看addEntry() 和 createEntry()方法
void addEntry(int hash, K key, V value, int bucketIndex) {
//调用HashMap的addEntry()
super.addEntry(hash, key, value, bucketIndex);
// Remove eldest entry if instructed
Entry eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
}
}
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry old = table[bucketIndex];
Entry e = new Entry<>(hash, key, value, old);
table[bucketIndex] = e;
//addBefore,其他都和HashMap的createEntry相关
e.addBefore(header);
size++;
}
从上面的代码看,LinkedHashMap和HashMap中,addEntry() 和 createEntry()的实现差不多,而LinkedHashMap多调用了一个新增的addBefore方法,这是LinkedHashMap存储排序最关键的地方
/**
* 将当前节点插入到existingEntry的前面
*/
private void addBefore(Entry existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
首先来看HashMap的遍历,HashMap的核心代码如下:
private abstract class HashIterator<E> implements Iterator<E> {
Entry next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
Entry current; // current entry
//当调用keySet().iterator()时,调用此代码
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
//从哈希表数组从上到下,查找第一个不为null的节点,并把next引用指向该节点
while (index < t.length && (next = t[index++]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
//当调用next时,会调用此代码
final Entry nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry e = next;
if (e == null)
throw new NoSuchElementException();
//如果当前节点的下一个节点为null,从节点处罚往下查找哈希表,找到第一个不为null的节点
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}
从这里可以看出,HashMap遍历时,按哈希表的每一个索引的链表从上往下遍历,由于HashMap的存储规则,最晚添加的节点都有可能在第一个索引的链表中,这就造成了HashMap的遍历时无序的。
然后来看LinkedHashMap的遍历:
private abstract class LinkedHashIterator<T> implements Iterator<T> {
//nextEntry指向头结点的下一个节点,也就是双向链表中的第一个节点
Entry nextEntry = header.after;
Entry lastReturned = null;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() {
return nextEntry != header;
}
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
LinkedHashMap.this.remove(lastReturned.key);
lastReturned = null;
expectedModCount = modCount;
}
//当调用next时,会调用此代码
Entry nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (nextEntry == header)
throw new NoSuchElementException();
从第一个节点开始,依次往下遍历
Entry e = lastReturned = nextEntry;
nextEntry = e.after;
return e;
}
}
可见LinkedHashMap的实现原理还是比较简单的,而且LinkedHashSet的内部完全就是通过LinkedHashMap来实现的