LinkedHashMap
是HashMap
的子類,與HashMap
有著同樣的存儲(chǔ)結(jié)構(gòu),但它加入了一個(gè)雙向鏈表的頭結(jié)點(diǎn),將所有put
到LinkedHashMap
的節(jié)點(diǎn)一一串成了一個(gè)雙向循環(huán)鏈表,因此它保留了節(jié)點(diǎn)插入的順序。可以按照訪問(wèn)順序和插入順序來(lái)進(jìn)行排序。LruCahe
就是基于LinkedHashMap
來(lái)實(shí)現(xiàn)的。
(圖片均來(lái)源于網(wǎng)絡(luò))
建議先閱讀HashMap源碼分析在來(lái)閱讀此篇文章。
先看看結(jié)構(gòu)圖,這有助于我們閱讀源碼:
HashMap
里面進(jìn)行key value
綁定的類是HashMapEntry
,在LinkedHashMap
則是LinkedHashMapEntry
,它繼承HashMapEntry
的一個(gè)類并且重寫(xiě)recordAccess recordRemoval
來(lái)進(jìn)行重新指向進(jìn)行排序,里面remove
函數(shù)主要對(duì)自身在鏈表中進(jìn)行移除,addBefore(LinkedHashMapEntry<K,V> existingEntry)
則是將自身插入到existingEntry
之前。
/**
* LinkedHashMap entry.
*/
private static class LinkedHashMapEntry<K,V> extends HashMapEntry<K,V> {
// These fields comprise the doubly linked list used for iteration.
LinkedHashMapEntry<K,V> before, after;
LinkedHashMapEntry(int hash, K key, V value, HashMapEntry<K,V> next) {
super(hash, key, value, next);
}
/**
* Removes this entry from the linked list.
*/
// 對(duì)自身在鏈表中進(jìn)行移除
private void remove() {
before.after = after;
after.before = before;
}
/**
* Inserts this entry before the specified existing entry in the list.
*/
//插入到LinkedHashMapEntry existingEntry之前
private void addBefore(LinkedHashMapEntry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
/**
* This method is invoked by the superclass whenever the value
* of a pre-existing entry is read by Map.get or modified by Map.set.
* If the enclosing Map is access-ordered, it moves the entry
* to the end of the list; otherwise, it does nothing.
*/
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
void recordRemoval(HashMap<K,V> m) {
remove();
}
}
有了上面的概念來(lái)看LinkedHashMap
的操作就相對(duì)比較容易了。
LinkedHashMap
put
的時(shí)候是調(diào)用HashMap
的put
函數(shù),只是自身實(shí)現(xiàn)了addEntry() createEntry()
來(lái)實(shí)現(xiàn)自身的邏輯
HashMap
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
int i = indexFor(hash, table.length);
for (HashMapEntry<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;
//調(diào)用子類LinkedHashMapEntry的recordAccess方法
e.recordAccess(this);
return oldValue;
}
}
modCount++;
//調(diào)用LinkedHashMap.addEntry方法
addEntry(hash, key, value, i);
return null;
}
/**
* Adds a new entry with the specified key, value and hash code to
* the specified bucket. It is the responsibility of this
* method to resize the table if appropriate.
*
* Subclass overrides this to alter the behavior of put method.
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
//擴(kuò)容
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
//增加新的key value 調(diào)用LinkedHashMap.createEntry
createEntry(hash, key, value, bucketIndex);
}
LinkedHashMap
/**
* This override alters behavior of superclass put method. It causes newly
* allocated entry to get inserted at the end of the linked list and
* removes the eldest entry if appropriate.
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
// Previous Android releases called removeEldestEntry() before actually
// inserting a value but after increasing the size.
// The RI is documented to call it afterwards.
// **** THIS CHANGE WILL BE REVERTED IN A FUTURE ANDROID RELEASE ****
//得到需要移除的對(duì)象
// Remove eldest entry if instructed
LinkedHashMapEntry<K,V> eldest = header.after;
//需要移除的對(duì)象是否是自身
if (eldest != header) {
boolean removeEldest;
size++;
try {
//得到是否移除的對(duì)象的標(biāo)識(shí)
removeEldest = removeEldestEntry(eldest);
} finally {
size--;
}
if (removeEldest) {
//調(diào)用HashMap的removeEntryForKey進(jìn)行移除
removeEntryForKey(eldest.key);
}
}
//調(diào)用HashMap的addEntry
super.addEntry(hash, key, value, bucketIndex);
}
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
/**
* Like addEntry except that this version is used when creating entries
* as part of Map construction or "pseudo-construction" (cloning,
* deserialization). This version needn't worry about resizing the table.
*
* Subclass overrides this to alter the behavior of HashMap(Map),
* clone, and readObject.
*/
//增加新的key value
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMapEntry<K,V> e = table[bucketIndex];
table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
size++;
}
我們主要看LinkedHashMap
里面的東西,涉及到HashMap
的就不重復(fù)說(shuō)了。
如果key
已經(jīng)在該LinkedHashMap
里面put
了,那么將會(huì)重新進(jìn)行賦值,并且調(diào)用LinkedHashMapEntry.recordAccess()
進(jìn)行排序,排序方式如果是訪問(wèn)順序(accessOrder==true)
,將會(huì)將其在鏈表中移除,然后添加到鏈尾部。最后在返回oldValue
。
如果key
不存在,則會(huì)調(diào)用LinkedHashMap.addEntry
,先得到需要移除的對(duì)象,就是header.after
,接著判斷需要移除的對(duì)象是否是自身,如果不是自生并且removeEldestEntry()函數(shù)返回的是true的話,那么將會(huì)調(diào)用HashMap
的removeEntryForKey
移除。移除這里后面在講。
接著調(diào)用HashMap.addEntry
進(jìn)行擴(kuò)容,在調(diào)用LinkedHashMap.createEntry
,這一步就是重新new
一個(gè)HashMapEntry
,添加到鏈尾就可以了。
如圖所示:
remove
過(guò)程也是調(diào)用HashMap.remove
,然后調(diào)用LinkedHashMapEntry.recordRemoval
函數(shù)來(lái)進(jìn)行刪除
/**
* Removes the mapping for the specified key from this map if present.
*
* @param key key whose mapping is to be removed from the map
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.getValue());
}
/**
* Removes and returns the entry associated with the specified key
* in the HashMap. Returns null if the HashMap contains no mapping
* for this key.
*/
final Entry<K,V> removeEntryForKey(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
int i = indexFor(hash, table.length);
HashMapEntry<K,V> prev = table[i];
HashMapEntry<K,V> e = prev;
while (e != null) {
HashMapEntry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
//調(diào)用LinkedHashMapEntry.recordRemoval
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
LinkedHashMapEntry中:
/**
* Removes this entry from the linked list.
*/
private void remove() {
before.after = after;
after.before = before;
}
void recordRemoval(HashMap<K,V> m) {
remove();
}
很簡(jiǎn)單,就是進(jìn)行一個(gè)指向。如圖所示:
get
/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code (key==null ? k==null :
* key.equals(k))}, then this method returns {@code v}; otherwise
* it returns {@code null}. (There can be at most one such mapping.)
*
* <p>A return value of {@code null} does not <i>necessarily</i>
* indicate that the map contains no mapping for the key; it's also
* possible that the map explicitly maps the key to {@code null}.
* The {@link #containsKey containsKey} operation may be used to
* distinguish these two cases.
*/
public V get(Object key) {
//調(diào)用HashMap的getEntry
LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
if (e == null)
return null;
e.recordAccess(this);
return e.value;
}
通過(guò)HashMap.getEntry
獲取對(duì)應(yīng)的值,然后調(diào)用LinkedHashMapEntry.recordAccess
排序。
有了LinkedHashMap
的基礎(chǔ)我們來(lái)看LruCache
的源碼就很好理解了
put
/**
* Caches {@code value} for {@code key}. The value is moved to the head of
* the queue.
*
* @return the previous value mapped by {@code key}.
*/
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++;
//根據(jù)key, value獲取大小,我們需要重寫(xiě)sizeOf自行賦值
size += safeSizeOf(key, value);
//加入到LinkedHashMap
previous = map.put(key, value);
//如果key已經(jīng)存在了previous 就!=null 就移除剛加的大小
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
//重寫(xiě)移除的邏輯
entryRemoved(false, key, previous, value);
}
//根據(jù)maxSize去除多余的元素
trimToSize(maxSize);
return previous;
}
remove
/**
* Removes the entry for {@code key} if it exists.
*
* @return the previous value mapped by {@code key}.
*/
public final V remove(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V previous;
synchronized (this) {
//從LinkedHashMap中移除
previous = map.remove(key);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, null);
}
return previous;
}
get
/**
* Returns the value for {@code key} if it exists in the cache or can be
* created by {@code #create}. If a value was returned, it is moved to the
* head of the queue. This returns null if a value is not cached and cannot
* be created.
*/
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
//從LinkedHashMap中取出元素
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
/*
* Attempt to create a value. This may take a long time, and the map
* may be different when create() returns. If a conflicting value was
* added to the map while create() was working, we leave that value in
* the map and release the created value.
*/
//如果沒(méi)有這個(gè)key,那么你可以重寫(xiě)create來(lái)創(chuàng)建一個(gè)新的value
V createdValue = create(key);
if (createdValue == null) {
return null;
}
//新的創(chuàng)建的value 添加到LinkedHashMap
synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue);
if (mapValue != null) {
// There was a conflict so undo that last put
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
參考鏈接:
Java容器框架分析(七)——LinkedHashSet與LinkedHashMap
【Java集合源碼剖析】LinkedHashmap源碼剖析
水平有限,文中有什么不對(duì)或者有什么建議希望大家能夠指出,謝謝!