您的位置:首页 > 其它

LinkedHashMap源码分析

2016-04-25 22:13 162 查看
接上一节关于HashMap的分析:

LinkedHashMap继承HashMap,所以HashMap是LinkedHashMap的父类,HashMap有的方法LinkedHashMap都有,LinkedHashMap也会重写某些方法,我们知道,HashMap底层数据结构是数组和链表,那么LinkedHashMap会额外维护一个按照插入顺序排序的双向循环列表。

public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}


这是linkedhashmap其中的一个构造方法,这里注意accessOrder这个参数,默认为false,那就按照插入的顺序排序,依靠那个双向循环列表来实现,如果传入accessOrder为true时,那将按照LRU算法来实现。

void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}

先来看这个重要的方法,这个方法是在哪里调用的呢,

public V get(Object key) {
Entry<K,V> e = (Entry<K,V>)getEntry(key);
if (e == null)
return null;
<span style="color:#ff0000;"> e.recordAccess(this);</span>
return e.value;
}
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
<span style="color:#ff0000;">e.recordAccess(this);</span>
return oldValue;
}
}

modCount++;
addEntry(hash, key, value, i);
return null;
}
put方法没有重写,所以put方法还是用的父类HashMap的put()方法,说明在put和get时会调用recordAccess方法,就是在访问元素时调用,我们接着看这个put方法,在里面调用了addEntry方法,所以linkedhashmap 重写了addEntry方法

void addEntry(int hash, K key, V value, int bucketIndex) {
createEntry(hash, key, value, bucketIndex);

// Remove eldest entry if instructed, else grow capacity if appropriate
Entry<K,V> eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
} else {
if (size >= threshold)
resize(2 * table.length);
}
}
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
}可以发现,addEntry调用了createEntry方法,这个就是加入元素的过程,显然,这里加入了两次,在hash数组中加入,还要addBefore,即在双向循环列表中加入,加在头结点之后,即元素末尾。接下来再来看这个方法:
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}


如果accessOrder为true,调用remove移除这个entry,并且把这个加到双向链表的末尾,这样就实现了LRU。
再来看这几行代码:
Entry<K,V> eldest = header.after;
if (<span style="color:#ff0000;">removeEldestEntry(eldest)</span>) {
removeEntryForKey(eldest.key);
} else {
if (size >= threshold)
resize(2 * table.length);
}
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
默认是返回false,如果我们自己实现的话,可以通过重写这个方法来设置删除头结点之后的元素,即最老元素。

看来,通过源码还是可以学习好多东西的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: