上一篇主要HashMap的源码解读。这次在研究下LinkedHashMap。
LinkedHashMap继承HashMap,增加一个双向链表来进行解决哈希冲突。
构造函数同HashMap,调用的HashMap的构造函数,增加了一个accessOrder变量,根据调用不同的构造函数来进行设置true或者false。同时复写了init()方法,
@Override
void init() {
header = new Entry<>(-1, null, null, null);
header.before = header.after = header;
}
在该方法中初始化了双向链表来进行解决哈希冲突。而accessOrder控制访问顺序,如果为true,则在get()元素的时候,会对元素进行重新排序,把刚刚get的元素调整为链表的最后一个节点,如果为false,则按照插入顺序进行存储元素。
先看下现在的链表的节点
private static class Entry extends HashMap.Entry {
//当前节点的前一个节点、后一个节点
Entry before, after;
Entry(int hash, K key, V value, HashMap.Entry next) {
super(hash, key, value, next);
}
/**
* 移除当前节点,就是将前一个节点的后节点指向当前节点的后节点
* 后一个节点的前节点指向当前节点的前节点
*/
private void remove() {
before.after = after;
after.before = before;
}
/**
* 插入节点
*/
private void addBefore(Entry existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
/**
*将当前节点变为最后一个节点
*/
void recordAccess(HashMap m) {
LinkedHashMap lm = (LinkedHashMap)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
void recordRemoval(HashMap m) {
remove();
}
}
存放数据仍然调用的是父类HashMap的put()方法。也就是在加入key和value的时候:
1)如果key为null,则将key对应的hash值为0,放到table数组的第0个位置;
2)如果key不为null,则根据hash函数得到对应的hash值,根据hash值找到对应的table数组的索引值
3)如果对应位置的链表元素有相应的key值,则直接更新数据
4)否则将新的key和value加入到链表中,而在LinkedHashMap复写了addEntry()和createEntry()
addEntry()增加了判断是否移除最先加入元素的逻辑。默认的为false
void addEntry(int hash, K key, V value, int bucketIndex) {
super.addEntry(hash, key, value, bucketIndex);
// 是否需要移除最先加入的元素
Entry eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
}
}
createEntry()就是创建该双向链表,和HashMap中创建单链表的思路一样
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;
e.addBefore(header);
size++;
}
同HashMap,直接调用的就是HashMap的get()
public V get(Object key) {
Entry e = (Entry)getEntry(key);
if (e == null)
return null;
e.recordAccess(this);
return e.value;
}
可以看到在取数据的时候,会根据 accessOrder是否为true来调整访问顺序,若为true,则会将当前访问的key对应的节点调整到链表最后一个节点。
线程同步 | key/value是否允许null | key/value是否允许重复 | |
LinkedHashMap | 不支持 | 允许 | key不允许重复/value允许重复 |
与HashMap的区别在于LinkedHashMap内部维持一个双向链表来解决哈希冲突和保持记录的插入顺序