日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

LinkedHashMap与HashMap 关系

發(fā)布時(shí)間:2024/1/8 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LinkedHashMap与HashMap 关系 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

LinkedHashMap源碼分析

1. LinkedHashMap 與 HashMap 的關(guān)系 2. LinkedHashMap 雙向鏈表的構(gòu)建過程 3. LinkedHashMap 刪除節(jié)點(diǎn)的過程 4. LinkedHashMap 如何維持訪問順序 5. LinkedHashMap - LRU (Least Recently Used) 最簡單的構(gòu)建方式

LinkedHashMap 與 HashMap 的關(guān)系

Java部分容器體系圖:

  • LinkedHashMap 直接繼承自HashMap

    1. hash算法定位, 2. 哈希表由數(shù)組和單鏈表構(gòu)成 3. 單鏈表長度超過8的時(shí)候轉(zhuǎn)化為紅黑樹 4. 擴(kuò)容體系(負(fù)載因子.)
  • 優(yōu)點(diǎn)

    1. 內(nèi)部維護(hù)了一個雙向鏈表,解決 HashMap不能隨保持遍歷順序和插入順序一致的問題 2. 元素的訪問順序也提供了相關(guān)支持,也就是我們常說的 LRU(最近最少使用)原則 (最簡單的LRU實(shí)現(xiàn)方式.)

LinkedHashMap 雙向鏈表的構(gòu)建過程

  • 假設(shè)圖片中紅黃箭頭代表元素添加順序,藍(lán)箭頭代表單鏈表各個元素的存儲順序。head 表示雙向鏈表頭部,tail 代表雙向鏈表尾部
  • LinkedHashMap 數(shù)據(jù)結(jié)構(gòu)和Hashmap 同樣為: 數(shù)組 + 單鏈表 + 紅黑樹,從上邊的圖片我們也可以看出 底層的存儲結(jié)構(gòu)并沒有發(fā)生變化
  • 唯一變化的是使用雙向鏈表(圖中紅黃箭頭部分)記錄了元素的添加順序,我們知道 HashMap 中的 Node 節(jié)點(diǎn)只有 next 指針,對于雙向鏈表而言只有 next 指針是不夠的,所以 LinkedHashMap 對于 Node 節(jié)點(diǎn)進(jìn)行了拓展
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);} }
  • LinkedHashMap 基本存儲單元 Entry<K,V> 繼承自 HashMap.Node<K,V>,并在此基礎(chǔ)上添加了 before 和 after 這兩個指針變量。
  • 這 before 變量在每次添加元素的時(shí)候?qū)溄由弦淮翁砑拥脑?#xff0c;而上一次添加的元素的 after 變量將指向該次添加的元素,來形成雙向鏈接。
  • 值得注意的是 LinkedHashMap 并沒有覆寫任何關(guān)于 HashMap put 方法。所以調(diào)用 LinkedHashMap 的 put 方法實(shí)際上調(diào)用了父類 HashMap 的方法。為了方便理解我們這里放一下 HashMap 的 putVal 方法。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {// 發(fā)生 hash 碰撞了Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode){....}else {//hash 值計(jì)算出的數(shù)組索引相同,但 key 并不同的時(shí)候 循環(huán)整個單鏈表for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {//遍歷到尾部// 創(chuàng)建新的節(jié)點(diǎn),拼接到鏈表尾部p.next = newNode(hash, key, value, null);....break;}//如果遍歷過程中找到鏈表中有個節(jié)點(diǎn)的 key 與 當(dāng)前要插入元素的 key 相同,//此時(shí) e 所指的節(jié)點(diǎn)為需要替換 Value 的節(jié)點(diǎn),并結(jié)束循環(huán)if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;//移動指針 p = e;}}//如果循環(huán)完后 e!=null 代表需要替換e所指節(jié)點(diǎn) Valueif (e != null) {V oldValue = e.value//保存原來的 Value 作為返回值// onlyIfAbsent 一般為 false 所以替換原來的 Valueif (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);//該方法在 LinkedHashMap 中的實(shí)現(xiàn)稍后說明return oldValue;}}//操作數(shù)增加++modCount;//如果 size 大于擴(kuò)容閾值則表示需要擴(kuò)容if (++size > threshold)resize();afterNodeInsertion(evict);return null; }

可以看出每次添加新節(jié)點(diǎn)的時(shí)候?qū)嶋H上是調(diào)用 newNode 方法生成了一個新的節(jié)點(diǎn),放到指定 hash 桶中,但是很明顯,HashMap 中 newNode 方法無法完成上述所講的雙向鏈表節(jié)點(diǎn)的間的關(guān)系,所以 LinkedHashMap 復(fù)寫了該方法

// HashMap newNode 中實(shí)現(xiàn) Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {return new Node<>(hash, key, value, next); }// LinkedHashMap newNode 的實(shí)現(xiàn) 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);// 將 Entry 接在雙向鏈表的尾部linkNodeLast(p);return p; }

可以看出雙向鏈表的操作一定在 linkNodeLast方法中實(shí)現(xiàn):

/** * 該引用始終指向雙向鏈表的頭部 */ transient LinkedHashMap.Entry<K,V> head;/** * 該引用始終指向雙向鏈表的尾部 */ transient LinkedHashMap.Entry<K,V> tail; // newNode 中新節(jié)點(diǎn),放到雙向鏈表的尾部 private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {// 添加元素之前雙向鏈表尾部節(jié)點(diǎn)LinkedHashMap.Entry<K,V> last = tail;// tail 指向新添加的節(jié)點(diǎn)tail = p;//如果之前 tail 指向 null 那么集合為空新添加的節(jié)點(diǎn) head = tail = pif (last == null)head = p;else {// 否則將新節(jié)點(diǎn)的 before 引用指向之前當(dāng)前鏈表尾部p.before = last;// 當(dāng)前鏈表尾部節(jié)點(diǎn)的 after 指向新節(jié)點(diǎn)last.after = p;} }

LinkedHashMap 鏈表創(chuàng)建步驟,可用上圖幾個步驟來描述,藍(lán)色部分是 HashMap 的方法,而橙色部分為 LinkedHashMap 獨(dú)有的方法。

  • 當(dāng)我們創(chuàng)建一個新節(jié)點(diǎn)之后,通過linkNodeLast方法,將新的節(jié)點(diǎn)與之前雙向鏈表的最后一個節(jié)點(diǎn)(tail)建立關(guān)系,在這部操作中我們?nèi)圆恢肋@個節(jié)點(diǎn)究竟儲存在哈希表表的何處,但是無論他被放到什么地方,節(jié)點(diǎn)之間的關(guān)系都會加入雙向鏈表。如上述圖中節(jié)點(diǎn) 3 和節(jié)點(diǎn) 4 那樣彼此擁有指向?qū)Ψ降囊?#xff0c;這么做就能確保了雙向鏈表的元素之間的關(guān)系即為添加元素的順序。

LinkedHashMap 刪除節(jié)點(diǎn)的操作

如插入操作一樣,LinkedHashMap 沒有重寫的 remove 方法,使用的仍然是 HashMap 中的代碼,我們先來回憶一下 HashMap 中的 remove 方法:

public V remove(Object key) {Node<K,V> e;return (e = removeNode(hash(key), key, null, false, true)) == null ?null : e.value; }// HashMap 中實(shí)現(xiàn)final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {Node<K,V>[] tab; Node<K,V> p; int n, index;//判斷哈希表是否為空,長度是否大于0 對應(yīng)的位置上是否有元素if ((tab = table) != null && (n = tab.length) > 0 &&(p = tab[index = (n - 1) & hash]) != null) {// node 用來存放要移除的節(jié)點(diǎn), e 表示下個節(jié)點(diǎn) k ,v 每個節(jié)點(diǎn)的鍵值Node<K,V> node = null, e; K k; V v;//如果第一個節(jié)點(diǎn)就是我們要找的直接賦值給 nodeif (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))node = p;else if ((e = p.next) != null) {// 遍歷紅黑樹找到對應(yīng)的節(jié)點(diǎn)if (p instanceof TreeNode)node = ((TreeNode<K,V>)p).getTreeNode(hash, key);else {//遍歷對應(yīng)的鏈表找到對應(yīng)的節(jié)點(diǎn)do {if (e.hash == hash &&((k = e.key) == key ||(key != null && key.equals(k)))) {node = e;break;}p = e;} while ((e = e.next) != null);}}// 如果找到了節(jié)點(diǎn)// !matchValue 是否不刪除節(jié)點(diǎn)// (v = node.value) == value ||(value != null && value.equals(v))) 節(jié)點(diǎn)值是否相同,if (node != null && (!matchValue || (v = node.value) == value ||(value != null && value.equals(v)))) {//刪除節(jié)點(diǎn) if (node instanceof TreeNode)((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);else if (node == p)tab[index] = node.next;elsep.next = node.next;++modCount;--size;afterNodeRemoval(node);// 注意這個方法 在 Hash表的刪除操作完成調(diào)用該方法return node;}}return null; }

LinkedHashMap 通過調(diào)用父類的 HashMap 的 remove 方法將 Hash 表的中節(jié)點(diǎn)的刪除操作完成即:

  • 獲取對應(yīng) key 的哈希值 hash(key),定位對應(yīng)的哈希桶的位置
  • 遍歷對應(yīng)的哈希桶中的單鏈表或者紅黑樹找到對應(yīng) key 相同的節(jié)點(diǎn),在最后刪除,并返回原來的節(jié)點(diǎn)。
  • 對于 afterNodeRemoval(node) HashMap 中是空實(shí)現(xiàn),而該方法,正是 LinkedHashMap 刪除對應(yīng)節(jié)點(diǎn)在雙向鏈表中的關(guān)系的操作:

    // 從雙向鏈表中刪除對應(yīng)的節(jié)點(diǎn) e 為已經(jīng)刪除的節(jié)點(diǎn) void afterNodeRemoval(Node<K,V> e) { LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;// 將 p 節(jié)點(diǎn)的前后指針引用置為 null 便于內(nèi)存釋放p.before = p.after = null;// p.before 為 null,表明 p 是頭節(jié)點(diǎn) if (b == null)head = a;else//否則將 p 的前驅(qū)節(jié)點(diǎn)連接到 p 的后驅(qū)節(jié)點(diǎn)b.after = a;// a 為 null,表明 p 是尾節(jié)點(diǎn)if (a == null)tail = b;else //否則將 a 的前驅(qū)節(jié)點(diǎn)連接到 b a.before = b; }

    因此 LinkedHashMap 節(jié)點(diǎn)刪除方式如下圖步驟一樣:

    LinkedHashMap 維護(hù)節(jié)點(diǎn)訪問順序

    LinkedHashMap 與 HashMap 添加和刪除元素的不同,可以看出除了維護(hù) Hash表中元素的關(guān)系以外,LinkedHashMap 還在添加和刪除元素的時(shí)候維護(hù)著一個雙向鏈表。那么這個雙向鏈表究竟有何用呢?我們來看下邊這個例子,我們對比一下在相同元素添加順序的時(shí)候,遍歷 Map 得到的結(jié)果:

    //Map<String, Integer> map = new HashMap<>();Map<String, Integer> map = new LinkedHashMap<>();// 使用三個參數(shù)的構(gòu)造法方法來指定 accessOrder 參數(shù)的值//Map<String, Integer> map = new LinkedHashMap<>(10,0.75f,true);map.put("老大", 1);map.put("老二", 2);map.put("老三", 3);map.put("老四", 4);Set<Map.Entry<String, Integer>> entrySet = map.entrySet();Iterator iter1 = entrySet.iterator();while (iter1.hasNext()) {Map.Entry entry = (Map.Entry) iter1.next();System.out.print("key: " + entry.getKey() + " ");System.out.println("value: " + entry.getValue());}System.out.println("老三的值為:" + map.get("老三"));System.out.println("老大的值為:" + map.put("老大",1000));Iterator iter2 = entrySet.iterator();while (iter2.hasNext()) {// 遍歷時(shí),需先獲取entry,再分別獲取key、valueMap.Entry entry = (Map.Entry) iter2.next();System.out.print("key: " + entry.getKey() + " ");System.out.println("value: " + entry.getValue());} /*** HashMap 遍歷結(jié)果*/ key: 老二 value: 2 key: 老四 value: 4 key: 老三 value: 3 key: 老大 value: 1 老三的值為:3 老大的值為:1 key: 老二 value: 2 key: 老四 value: 4 key: 老三 value: 3 key: 老大 value: 1000/*** LinkedHashMap 遍歷結(jié)果*/ key: 老大 value: 1 key: 老二 value: 2 key: 老三 value: 3 key: 老四 value: 4 老三的值為:3 老大的值為:1 key: 老大 value: 1000 key: 老二 value: 2 key: 老三 value: 3 key: 老四 value: 4

    由上述方法結(jié)果可以看出:

  • HashMap 的遍歷結(jié)果是跟添加順序并無關(guān)系
  • LinkedHashMap 的遍歷結(jié)果就是添加順序
  • 這就是雙向鏈表的作用。雙向鏈表能做的不僅僅是這些,在介紹雙向鏈表維護(hù)訪問順序前我們看來看一個重要的參數(shù):

    final boolean accessOrder;// 是否維護(hù)雙向鏈表中的元素訪問順序

    該方法隨 LinkedHashMap 構(gòu)造參數(shù)初始化,accessOrder 默認(rèn)值為 false,我們可以通過三個參數(shù)構(gòu)造方法指定該參數(shù)的值,參數(shù)定義為 final 說明外部不能改變

    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();accessOrder = false;putMapEntries(m, false); }//可以指定 LinkedHashMap 雙向鏈表維護(hù)節(jié)點(diǎn)訪問順序的構(gòu)造參數(shù) public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {super(initialCapacity, loadFactor);this.accessOrder = accessOrder; }//第一次遍歷 key: 老大 value: 1 key: 老二 value: 2 key: 老三 value: 3 key: 老四 value: 4老三的值為:3 老大的值為:1//第二次遍歷 key: 老二 value: 2 key: 老四 value: 4 key: 老三 value: 3 key: 老大 value: 1000
    • 可以看出當(dāng)我們使用 access 為 true 后,我們訪問元素的順序?qū)谙麓伪闅v的時(shí)候體現(xiàn),最后訪問的元素將最后獲得。
    • 其實(shí)這一切在 HashMap 源碼中也早有伏筆, 還記得我們在每次 putVal/get/repalce 最后都有一個 void afterNodeAccess(Node<K,V> e) 方法,該方法在 HashMap 中是空實(shí)現(xiàn),但是在 LinkedHasMap 中該后置方法,將作為維護(hù)節(jié)點(diǎn)訪問順序的重要方法
    //將被訪問節(jié)點(diǎn)移動到鏈表最后 void afterNodeAccess(Node<K,V> e) { // move node to lastLinkedHashMap.Entry<K,V> last;if (accessOrder && (last = tail) != e) {LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;//訪問節(jié)點(diǎn)的后驅(qū)置為 null p.after = null;//如訪問節(jié)點(diǎn)的前驅(qū)為 null 則說明 p = headif (b == null)head = a;elseb.after = a;//如果 p 不為尾節(jié)點(diǎn) 那么將 a 的前驅(qū)設(shè)置為 b if (a != null)a.before = b;elselast = b;if (last == null)head = p;else {p.before = last;last.after = p;}tail = p;// 將 p 接在雙向鏈表的最后++modCount;} }

    我們以下圖舉例看下整個 afterNodeAccess 過程是是怎么樣的,比如我們該次操作訪問的是 13 這個節(jié)點(diǎn),而 14 是其后驅(qū),11 是其前驅(qū),且 tail = 14 。在通過 get 訪問 13 節(jié)點(diǎn)后, 13變成了 tail 節(jié)點(diǎn),而14變成了其前驅(qū)節(jié)點(diǎn),相應(yīng)的 14的前驅(qū)變成 11 ,11的后驅(qū)變成了14, 14的后驅(qū)變成了13.

    • 由此我們得知,LinkedHashMap 通過afterNodeAccess 這個后置操作,可以在 accessOrde = true 的時(shí)候,使雙向鏈表維護(hù)哈希表中元素的訪問順序
    // HashIterator nextNode 方法final Node<K,V> nextNode() {Node<K,V>[] t;Node<K,V> e = next;if (modCount != expectedModCount)throw new ConcurrentModificationException();if (e == null)throw new NoSuchElementException();//遍歷 table 尋找下個存有元素的 hash桶 if ((next = (current = e).next) == null && (t = table) != null) {do {} while (index < t.length && (next = t[index++]) == null);}return e;}// LinkedHashIterator nextNode 方法 final LinkedHashMap.Entry<K,V> nextNode() {LinkedHashMap.Entry<K,V> e = next;if (modCount != expectedModCount)throw new ConcurrentModificationException();if (e == null)throw new NoSuchElementException();current = e;//直接指向了當(dāng)前節(jié)點(diǎn)的 after 后驅(qū)節(jié)點(diǎn)next = e.after;return e;}

    更為明顯的我們可以查看兩者的 containsValue 方法:

    //LinkedHashMap 中 containsValue 的實(shí)現(xiàn) public boolean containsValue(Object value) {// 直接遍歷雙向鏈表去尋找對應(yīng)的節(jié)點(diǎn)for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {V v = e.value;if (v == value || (value != null && value.equals(v)))return true;}return false; } //HashMap 中 containsValue 的實(shí)現(xiàn) public boolean containsValue(Object value) {Node<K,V>[] tab; V v;if ((tab = table) != null && size > 0) {//遍歷 哈希桶索引for (int i = 0; i < tab.length; ++i) //遍歷哈希桶中鏈表或者紅黑樹for (Node<K,V> e = tab[i]; e != null; e = e.next) {if ((v = e.value) == value ||(value != null && value.equals(v)))return true;}}}return false; }

    Java 中最簡單的 LRU 構(gòu)建方式

    • LRU 是 Least Recently Used 的簡稱,即近期最少使用
    • 相信做 Android 的同學(xué)一定知道 LruCache 這個東西, Glide 的三級緩存中內(nèi)存緩存中也使用了這個 LruCache 類。

    LRU 算法實(shí)現(xiàn)的關(guān)鍵就像它名字一樣,當(dāng)達(dá)到預(yù)定閾值的時(shí)候,這個閾值可能是內(nèi)存不足,或者容量達(dá)到最大,找到最近最少使用的存儲元素進(jìn)行移除,保證新添加的元素能夠保存到集合中。

    • 下面我們來講解下,Java 中 LRU 算法的最簡單的實(shí)現(xiàn)。
    • 我們還記得在每次調(diào)用 HashMap 的 putVal 方法添加完元素后還有個后置操作,void afterNodeInsertion(boolean evict) { } 就是這個方法。

    LinkedHashMap 重寫了此方法:

    // HashMap 中 putVal 方法實(shí)現(xiàn) evict 傳遞的 true,表示表處于創(chuàng)建模式。 public V put(K key, V value) {return putVal(hash(key), key, value, false, true); }final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) { .... }//evict 由上述說明大部分情況下都傳 true 表示表處于創(chuàng)建模式 void afterNodeInsertion(boolean evict) { // possibly remove eldestLinkedHashMap.Entry<K,V> first;//由于 evict = true 那么當(dāng)鏈表不為空的時(shí)候 且 removeEldestEntry(first) 返回 true 的時(shí)候進(jìn)入if 內(nèi)部if (evict && (first = head) != null && removeEldestEntry(first)) {K key = first.key;removeNode(hash(key), key, null, false, true);//移除雙向鏈表中處于 head 的節(jié)點(diǎn)} }//LinkedHashMap 默認(rèn)返回 false 則不刪除節(jié)點(diǎn)。 返回 true 雙向鏈表中處于 head 的節(jié)點(diǎn) protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {return false; }
    • 由上述源碼可以看出,如果如果 removeEldestEntry(Map.Entry<K,V> eldest) 方法返回值為 true 的時(shí)候,當(dāng)我們添加一個新的元素之后,afterNodeInsertion這個后置操作,將會刪除雙向鏈表最初的節(jié)點(diǎn),也就是 head 節(jié)點(diǎn)。
    • 那么我們就可以從 removeEldestEntry 方法入手來構(gòu)建我們的 LruCache 。
    public class LruCache<K, V> extends LinkedHashMap<K, V> {private static final int MAX_NODE_NUM = 2<<4;private int limit;public LruCache() {this(MAX_NODE_NUM);}public LruCache(int limit) {super(limit, 0.75f, true);this.limit = limit;}public V putValue(K key, V val) {return put(key, val);}public V getValue(K key) {return get(key);}/*** 判斷存儲元素個數(shù)是否預(yù)定閾值* @return 超限返回 true,否則返回 false*/@Overrideprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {return size() > limit;} }
    • 我們構(gòu)建了一個 LruCache 類, 他繼承自 LinkedHashMap 在構(gòu)建的時(shí)候,調(diào)用了 LinkedHashMap 的三個參數(shù)的構(gòu)造方法且 accessOrder 傳入 true,并覆寫了 removeEldestEntry 方法,當(dāng) Map 中的節(jié)點(diǎn)個數(shù)超過我們預(yù)定的閾值時(shí)候在 putValue 將會執(zhí)行 afterNodeInsertion 刪除最近沒有訪問的元素。
    • 測試一下
    //構(gòu)建一個閾值為 3 的 LruCache 類LruCache<String,Integer> lruCache = new LruCache<>(3);lruCache.putValue("老大", 1);lruCache.putValue("老二", 2);lruCache.putValue("老三", 3);lruCache.getValue("老大");//超過指定 閾值 3 再次添加元素的 將會刪除最近最少訪問的節(jié)點(diǎn)lruCache.putValue("老四", 4);System.out.println("lruCache = " + lruCache);

    運(yùn)行結(jié)果當(dāng)然是刪除 key 為 “老二” 的節(jié)點(diǎn):

    lruCache = {老三=3, 老大=1, 老四=4}

    六、 總結(jié)

  • LinkedHashMap 擁有與 HashMap 相同的底層哈希表結(jié)構(gòu),即數(shù)組 + 單鏈表 + 紅黑樹,也擁有相同的擴(kuò)容機(jī)制。
  • LinkedHashMap 相比 HashMap 的拉鏈?zhǔn)酱鎯Y(jié)構(gòu),內(nèi)部額外通過 Entry 維護(hù)了一個雙向鏈表。
  • HashMap 元素的遍歷順序不一定與元素的插入順序相同,而 LinkedHashMap 則通過遍歷雙向鏈表來獲取元素,所以遍歷順序在一定條件下等于插入順序。
  • LinkedHashMap 可以通過構(gòu)造參數(shù) accessOrder 來指定雙向鏈表是否在元素被訪問后改變其在雙向鏈表中的位置
  • 1. 每次添加新節(jié)點(diǎn)的時(shí)候?qū)嶋H上是調(diào)用 newNode 方法生成了一個新的節(jié)點(diǎn),LinkedHashMap 復(fù)寫了該方法,雙向鏈 表的操作一定在**linkNodeLast方法中實(shí)現(xiàn)**:將新的節(jié)點(diǎn)與之前雙向鏈表的最后一個節(jié)點(diǎn)(tail)建立關(guān)系,彼 此擁有指向?qū)Ψ降囊?#xff0c;這么做就能確保了雙向鏈表的元素之間的關(guān)系即為添加元素的順序。2. LinkedHashMap 刪除節(jié)點(diǎn)的操作,對于 afterNodeRemoval(node) HashMap 中是空實(shí)現(xiàn),而該方法,正是 LinkedHashMap 刪除對應(yīng)節(jié)點(diǎn)在雙向鏈表中的關(guān)系的操作3. LinkedHashMap 與 HashMap 添加和刪除元素的不同,可以看出除了維護(hù) Hash表中元素的關(guān)系以外, LinkedHashMap 還在添加和刪除元素的時(shí)候維護(hù)著一個雙向鏈表。4. 該方法隨 LinkedHashMap 構(gòu)造參數(shù)初始化,accessOrder 默認(rèn)值為 false。--LinkedHashMap 通過 afterNodeAccess 這個**后置操作**,可以在 accessOrde = true 的時(shí)候,使雙向鏈表維護(hù)哈希表中元素的訪問順序。5. LinkedHashMap 的迭代器,由于有雙向鏈表的存在,它相比 HashMap 遍歷節(jié)點(diǎn)的方式更為高效--直接指向了當(dāng)前 節(jié)點(diǎn)的 after 后驅(qū)節(jié)點(diǎn)

    本文參考:@凱旋之戀 https://www.jianshu.com/p/1038f42b064c

    總結(jié)

    以上是生活随笔為你收集整理的LinkedHashMap与HashMap 关系的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。