LinkedHashMap是数组+双向链表的数据结构,继承HashMap并重写部分方法来达到双向链表的特性。
LindedHashMap重写了HashMap的Entry,使其具有双向链表的特性,自身维护的属性只有两个属性,其他均继承自HashMap。
/**
* The head of the doubly linked list.
*/
private transient Entry header;
/**
* The iteration ordering method for this linked hash map: true
* for access-order, false for insertion-order.
*
* @serial
*/
private final boolean accessOrder;
LinkedHashMap的构造方法都是调用父类即HashMap中相应的构造进行初始化,唯一不同的是在HashMap中的init()没有作用,而LinkedHashMap重写了该方法进行初始化双向链表的特性。
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
public LinkedHashMap() {
super();
accessOrder = false;
}
public LinkedHashMap(Map extends K, ? extends V> m) {
super(m);
accessOrder = false;
}
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
这几个构造就不做分析了,看下init()方法
void init() {
header = new Entry<>(-1, null, null, null);
header.before = header.after = header;
}
这里初始化了一个空的头节点header。before和after都指向自己。
LinkedHashMap中没有重写put,采用父类即HashMap中的方法,但对其中的方法进行重写使其适用自身特性。
关于HashMap的分析可以参考HashMap浅析
void addEntry(int hash, K key, V value, int bucketIndex) {
//调用父类方法进行数据添加,涉及一些方法重新往下看
super.addEntry(hash, key, value, bucketIndex);
// Remove eldest entry if instructed
Entry eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
}
}
void transfer(HashMap.Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
//遍历整个双向链表进行数据移动
for (Entry e = header.after; e != header; e = e.after) {
if (rehash)
e.hash = (e.key == null) ? 0 : hash(e.key);
int index = indexFor(e.hash, newCapacity);
//注意这里是next而不是after
e.next = newTable[index];
newTable[index] = e;
}
}
举例讲解下上述代码的过程。如图中第一行header、Entry(B)、Entry©,第二行为新数组的下边位置(下边为重新计算)
void createEntry(int hash, K key, V value, int bucketIndex) {
//将当前位置数据放到中间变量old中。
HashMap.Entry old = table[bucketIndex];
//新创建并将old放到e.next上
Entry e = new Entry<>(hash, key, value, old);
table[bucketIndex] = e;
//将新增加的节点放到链表的尾部
e.addBefore(header);
size++;
}
//将新节点放到existingEntry(header)前边即链表的尾部
private void addBefore(Entry existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
相较于put,get方式就比较简单了,LinkedHashMap重写了get方法
public V get(Object key) {
//调用父类的获取如果有则返回,若没有则返回null
Entry e = (Entry)getEntry(key);
if (e == null)
return null;
e.recordAccess(this);
return e.value;
}
此时存在两种情况:
HashMap与LinkedHashMap的区别: