Java LinkedHashMap类源码解析
摘要: LinkedHashMap繼承了HashMap,他在HashMap的基礎(chǔ)上增加了一個(gè)雙向鏈表的結(jié)構(gòu),鏈表默認(rèn)維持key插入的順序,重復(fù)的key值插入不會(huì)改變順序,適用于使用者需要返回一個(gè)順序相同的map對(duì)象的情況。
LinkedHashMap繼承了HashMap,他在HashMap的基礎(chǔ)上增加了一個(gè)雙向鏈表的結(jié)構(gòu),鏈表默認(rèn)維持key插入的順序,重復(fù)的key值插入不會(huì)改變順序,適用于使用者需要返回一個(gè)順序相同的map對(duì)象的情況。還可以生成access-order順序的版本,按照最近訪問順序來存儲(chǔ),剛被訪問的結(jié)點(diǎn)處于鏈表的末尾,適合LRU,put get compute merge都算作一次訪問,其中put key值相同的結(jié)點(diǎn)也算作一次訪問,replace只有在換掉一個(gè)鍵值對(duì)的時(shí)候才算一次訪問,putAll產(chǎn)生的訪問順序取決于原本map的迭代器實(shí)現(xiàn)。
在插入鍵值對(duì)時(shí),可以通過對(duì)removeEldestEntry重寫來實(shí)現(xiàn)新鍵值對(duì)插入時(shí)自動(dòng)刪除最舊的鍵值對(duì)
擁有HashMap提供的方法,迭代器因?yàn)槭峭ㄟ^遍歷雙向鏈表,所以額外開銷與size成正比與capacity無關(guān),因此選擇過大的初始大小對(duì)于遍歷時(shí)間的增加沒有HashMap嚴(yán)重,后者的遍歷時(shí)間依賴與capacity。
同樣是非線程安全方法,對(duì)于LinkedHashMap來說,修改結(jié)構(gòu)的操作除了增加和刪除鍵值對(duì)外,還有對(duì)于access-order時(shí)進(jìn)行了access導(dǎo)致迭代器順序改變,主要是get操作,對(duì)于插入順序的來說,僅僅修改一個(gè)已有key值的value值不是一個(gè)修改結(jié)構(gòu)的操作,但對(duì)于訪問順序,put和get已有的key值會(huì)改變順序。迭代器也是fail-fast設(shè)計(jì),但是fail-fast只是一個(gè)調(diào)試功能,一個(gè)設(shè)計(jì)良好的程序不應(yīng)該出現(xiàn)這個(gè)錯(cuò)誤
因?yàn)镠ashMap加入了TreeNode,所以現(xiàn)在LinkedHashMap也有這個(gè)功能
以下描述中的鏈表,若無特別說明都是指LinkedHashMap的雙向鏈表
先來看一下基本結(jié)構(gòu),每個(gè)鍵值對(duì)加入了前后指針,集合加入了頭尾指針來形成雙向鏈表,accessOrder代表鏈表是以訪問順序還是插入順序存儲(chǔ)
static class Entry<K,V> extends HashMap.Node<K,V> {Entry<K,V> before, after;//增加了先后指針來形成雙向鏈表Entry(int hash, K key, V value, Node<K,V> next) {super(hash, key, value, next);}}/*** The head (eldest) of the doubly linked list.頭部*/transient LinkedHashMap.Entry<K,V> head;/*** The tail (youngest) of the doubly linked list.尾部*/transient LinkedHashMap.Entry<K,V> tail;//true訪問順序 false插入順序final boolean accessOrder; 復(fù)制代碼然后是幾個(gè)內(nèi)部方法。linkNodeLast將p連接到鏈表尾部
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {LinkedHashMap.Entry<K,V> last = tail;tail = p;if (last == null)head = p;//原本鏈表為空則p同時(shí)為頭部else {p.before = last;last.after = p;}} 復(fù)制代碼transferLinks用dst替換src
private void transferLinks(LinkedHashMap.Entry<K,V> src,LinkedHashMap.Entry<K,V> dst) {LinkedHashMap.Entry<K,V> b = dst.before = src.before;LinkedHashMap.Entry<K,V> a = dst.after = src.after;if (b == null)head = dst;elseb.after = dst;if (a == null)tail = dst;elsea.before = dst;} 復(fù)制代碼reinitialize在調(diào)用HashMap方法的基礎(chǔ)上,將head和tail設(shè)為null
void reinitialize() {super.reinitialize();head = tail = null;} 復(fù)制代碼newNode生成一個(gè)LinkedHashMap結(jié)點(diǎn),next指向e,插入到LinkedHashMap鏈表末端
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {LinkedHashMap.Entry<K,V> p =new LinkedHashMap.Entry<K,V>(hash, key, value, e);//新建一個(gè)鍵值對(duì),next指向elinkNodeLast(p);//p插入到LinkedHashMap鏈表末端return p;} 復(fù)制代碼replacementNode根據(jù)原結(jié)點(diǎn)生成一個(gè)LinkedHashMap結(jié)點(diǎn)替換原結(jié)點(diǎn)
Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;LinkedHashMap.Entry<K,V> t =new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);//生成一個(gè)新的鍵值對(duì)next是給出的next參數(shù)transferLinks(q, t);//用t替換qreturn t;} 復(fù)制代碼newTreeNode生成一個(gè)TreeNode結(jié)點(diǎn),next指向next,插入到LinkedHashMap鏈表末端
TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);//生成一個(gè)TreeNode,next指向參數(shù)nextlinkNodeLast(p);//p插入到LinkedHashMap鏈表末端return p;} 復(fù)制代碼replacementTreeNode根據(jù)結(jié)點(diǎn)p生成一個(gè)新的TreeNode,next設(shè)為給定的next,替換原本的p
TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);transferLinks(q, t);//根據(jù)結(jié)點(diǎn)p生成一個(gè)新的TreeNode,next設(shè)為給定的next,替換原本的preturn t;} 復(fù)制代碼afterNodeRemoval從LinkedHashMap的鏈上移除結(jié)點(diǎn)e
void afterNodeRemoval(Node<K,V> e) { LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;p.before = p.after = null;if (b == null)head = a;elseb.after = a;if (a == null)tail = b;elsea.before = b;} 復(fù)制代碼afterNodeInsertion可能移除最舊的結(jié)點(diǎn),需要evict為true同時(shí)鏈表不為空同時(shí)removeEldestEntry需要重寫
void afterNodeInsertion(boolean evict) { LinkedHashMap.Entry<K,V> first;if (evict && (first = head) != null && removeEldestEntry(first)) {//removeEldestEntry需要重寫才從發(fā)揮作用,否則一定返回falseK key = first.key;//移除鏈表頭部的結(jié)點(diǎn)removeNode(hash(key), key, null, false, true);}} 復(fù)制代碼afterNodeAccess在訪問過后將結(jié)點(diǎn)e移動(dòng)到鏈表尾部,需要Map是access-order,若移動(dòng)成功則增加modCount
void afterNodeAccess(Node<K,V> e) { LinkedHashMap.Entry<K,V> last;if (accessOrder && (last = tail) != e) {//Map是access-order同時(shí)e不是鏈表的尾部LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;p.after = null;if (b == null)//將結(jié)點(diǎn)e從鏈表中剪下head = a;elseb.after = a;if (a != null)a.before = b;elselast = b;if (last == null)head = p;else {p.before = last;last.after = p;}tail = p;//結(jié)點(diǎn)e移動(dòng)到鏈表尾部++modCount;//因?yàn)橛衋ccess-order下結(jié)點(diǎn)被移動(dòng),所以增加modCount}} 復(fù)制代碼構(gòu)造函數(shù)方面,accessOrder默認(rèn)是false插入順序,初始大小為16,負(fù)載因子為0.75,這里是同HashMap。復(fù)制構(gòu)造也是調(diào)用了HashMap.putMapEntries方法
containsValue遍歷鏈表尋找相等的value值,這個(gè)操作一定不會(huì)造成結(jié)構(gòu)改變
public boolean containsValue(Object value) {for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {//檢查同樣是根據(jù)LinkedHashMap提供的鏈表順序進(jìn)行遍歷V v = e.value;if (v == value || (value != null && value.equals(v)))return true;}return false;} 復(fù)制代碼get方法復(fù)用HashMap的getNode方法,若找到結(jié)點(diǎn)且Map是訪問順序時(shí),要將訪問的結(jié)點(diǎn)放到鏈表最后,若沒找到則返回null。而getOrDefault僅有的區(qū)別是沒找到時(shí)返回defaultValue
public V get(Object key) {Node<K,V> e;if ((e = getNode(hash(key), key)) == null)//復(fù)用HashMap的getNode方法return null;if (accessOrder)afterNodeAccess(e);//access-order時(shí)將e放到隊(duì)尾return e.value;}public V getOrDefault(Object key, V defaultValue) {Node<K,V> e;if ((e = getNode(hash(key), key)) == null)return defaultValue;//復(fù)用HashMap的getNode方法,若沒有找到對(duì)應(yīng)的結(jié)點(diǎn)則返回defaultValueif (accessOrder)afterNodeAccess(e);//access-order時(shí)將e放到隊(duì)尾return e.value;} 復(fù)制代碼clear方法在HashMap的基礎(chǔ)上要把head和tail設(shè)為null
public void clear() {super.clear();head = tail = null;} 復(fù)制代碼removeEldestEntry在put和putAll插入鍵值對(duì)時(shí)調(diào)用,原本是一定返回false的,如果要自動(dòng)刪除最舊的鍵值對(duì)要返回true,需要進(jìn)行重寫。比如下面這個(gè)例子,控制size不能超過100
private static final int MAX_ENTRIES = 100;protected boolean removeEldestEntry(Map.Entry eldest) {return size() > MAX_ENTRIES;} 復(fù)制代碼下面兩個(gè)方法和HashMap相似,返回key的Set和value的Collection還有返回鍵值對(duì)的Set,這個(gè)是直接引用,所以對(duì)它們的remove之類的修改會(huì)直接反饋到LinkedHashMap上
public Set<K> keySet() {Set<K> ks = keySet;if (ks == null) {ks = new LinkedKeySet();keySet = ks;}return ks;//返回key值的set}public Collection<V> values() {Collection<V> vs = values;if (vs == null) {vs = new LinkedValues();values = vs;}return vs;//返回一個(gè)包含所有value值的Collection}public Set<Map.Entry<K,V>> entrySet() {Set<Map.Entry<K,V>> es;return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;//返回一個(gè)含有所有鍵值對(duì)的Set} 復(fù)制代碼檢查HashMap的putVal方法,我們可以看到在找到了相同key值并修改value值時(shí)會(huì)調(diào)用afterNodeAccess,對(duì)于access-order會(huì)改變結(jié)點(diǎn)順序
if (e != null) { // 找到了相同的key則修改value值并返回舊的valueV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}復(fù)制代碼總結(jié)
以上是生活随笔為你收集整理的Java LinkedHashMap类源码解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 20180917
- 下一篇: Java Bean与Map之间相互转化的