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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

容器源码解析之LinkedHashMap(九)

發布時間:2024/2/28 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 容器源码解析之LinkedHashMap(九) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1、LinkedHashMap的繼承結構

public class LinkedHashMap<K,V>extends HashMap<K,V>implements Map<K,V>

從結構可以看出,LinkedHashMap繼承HashMap并實現了Map接口。

2、LInkedHashMap構造函數

下面幾個是LinkedHashMap的構造函數,每個構造函數都是直接調用父類HashMap的構造函數來完成相應的初始化工作。唯一的不同在于對變量:accessOrder 指定為 false。

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);}public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {super(initialCapacity, loadFactor);this.accessOrder = accessOrder;}

構造函數中所提到的accessOrder

/*** The iteration ordering method for this linked hash map: <tt>true</tt>* for access-order, <tt>false</tt> for insertion-order.** @serial*/final boolean accessOrder;

根據注釋,理解如下:

accessOrder,簡單說就是這個用來控制元素的順序,
accessOrder為true: 表示按照訪問的順序來,也就是誰最先訪問,就排在第一位
accessOrder為false表示按照存放順序來,就是你put元素的時候的順序。

3、LinkedHashMap類中的內部類Entry

Entry類繼承的是HashMap.Node類,且引入了兩個屬性before/after,HashMap就是利用HashMap.Node類實現的單鏈表,再加上借助一個存儲HashMap.Node的數組就實現了“數組鏈表”的結合體。而LinkedHashMap引入before/after兩個屬性,可以看出,是準備實現雙向鏈表的,因此LinkedHashMap將是“數組和雙鏈表”的結合體。

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);}}//下面為HashMap的Node類static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next;Node(int hash, K key, V value, Node<K,V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}public final K getKey() { return key; }public final V getValue() { return value; }public final String toString() { return key + "=" + value; }public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}public final V setValue(V newValue) {V oldValue = value;value = newValue;return oldValue;}public final boolean equals(Object o) {if (o == this)return true;if (o instanceof Map.Entry) {Map.Entry<?,?> e = (Map.Entry<?,?>)o;if (Objects.equals(key, e.getKey()) &&Objects.equals(value, e.getValue()))return true;}return false;}}

4、put方法

既然是集合,肯定會有put方法來往容器中添加元素,在LinkedHashMap搜索了半天,沒有找到,在找put方法的過程中,發現有get方法,怎么會沒有put方法呢??想了下,唯一的可能就是LinkedHashMap繼承了HashMap沒有重寫HashMap中的put方法也。

下面我們貼出HashMap的put方法,看看這個put方法在LinkedHashMap中是如何來工作的。

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) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;//重新開辟一個Node<K,V>的數組if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {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)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;//1------LinkedHashMapafterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();//2、LinkedHashMapafterNodeInsertion(evict);return null;}

上面就是HashMap中put方法的代碼,在看LinkedHashMap源碼之前看HashMap的時候,看到put方法中調用afterNodeAccess(e)和afterNodeInsertion(evict);而這兩個方法在HashMap是兩個空實現的方法:

// Callbacks to allow LinkedHashMap post-actionsvoid afterNodeAccess(Node<K,V> p) { }void afterNodeInsertion(boolean evict) { }

當時,還在郁悶,為什么調用了兩個空實現的函數呢??

現在,看了LinkedHashMap的源碼,原來這兩個函數是專門給LinkedHashMap重寫用的。只要LinkedHashMap重寫了這兩個函數,也就完成了LinkedHashMap自己的put方法實現。

put方法的思路在HashMap中已經分析過了,大致如下:根據key的hash值得到存儲位置,然后判斷該存儲位置是否已經有了元素,如果有了,則在該位置的鏈表中,找是否含有該key,如果有該key,則更新value。如果沒有找到,則將節點插入到該位置的鏈表頭。

現在,由于針對的是LinkedHashMap,因此思路稍微發生了點變化,在鏈表中找到key之后調用了afterNodeAccess函數,LinkedHashMap中的此函數不再為空,如果沒有找到key,在插入節點之后返回之前,調用了afterNodeInsertion方法。

下面我們就來看下這兩個函數的具體內容。

afterNodeAccess(Node

void afterNodeAccess(Node<K,V> e) { // move node to lastLinkedHashMap.Entry<K,V> last;//accessOrder 為true時,才會進入下面if的語句塊中if (accessOrder && (last = tail) != e) {LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;p.after = null;/*調整鏈表指向,將e的前一個節點和后一個節點連接起來*/if (b == null)//前一個節點為null的情況head = a;elseb.after = a;if (a != null)//后一個節點不為null的情況a.before = b;elselast = b;//將p節點放在鏈表的最后面if (last == null)head = p;else {p.before = last;last.after = p;}tail = p;++modCount;}}

下面介紹afterNodeInsertion(boolean evict)

從源碼中可以看到,這個函數相當于什么都沒有做。

原因為:removeEldestEntry函數一直返回false,導致這個函數afterNodeInsertion的if條件也就一直為false。

因此,不知道這個函數為什么這么寫,分析了下,由于LinkedHashMap當accessOrder為false時,要按照添加元素的順序進行維護鏈表,而HashMap就是直接將新節點放入到鏈表頭,因此這個函數也就不需要做什么了。

void afterNodeInsertion(boolean evict) { // possibly remove eldestLinkedHashMap.Entry<K,V> first;if (evict && (first = head) != null && removeEldestEntry(first)) {K key = first.key;removeNode(hash(key), key, null, false, true);}}protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {return false;}

以上就是關于LinkedHashMap的put方法,

LinkedHashMap與HashMap的區別真心不大,從put方法上可以看出,唯一的區別在于,如果我們設置了accessOrder = true,則會將訪問的節點放入到鏈表的尾結點處,其它的都一樣。

5、get方法

get方法的思路雖然對HashMap的get方法進行了重寫,但基本與HashMap的思路一致:也是直接調用getNode獲取到節點對象,然后返回其值。

但是,在LinkedHashMap中,由于需要有順序需要維護,因此,當accessOrder = true 時,則需要調用afterNodeAccess(e)方法將此節點放到雙向鏈表的末尾。而如果accessOrder = false.則完全與HashMap類中的get方法一模一樣。

public V get(Object key) {Node<K,V> e;if ((e = getNode(hash(key), key)) == null)return null;if (accessOrder)afterNodeAccess(e);return e.value;}

6、getOrDefault方法

getOrDefault方法與get方法唯一的區別在于,如果key不存在,則返回默認值而不是返回null。

public V getOrDefault(Object key, V defaultValue) {Node<K,V> e;if ((e = getNode(hash(key), key)) == null)return defaultValue;if (accessOrder)afterNodeAccess(e);return e.value;}

LinkedHashMap類中其它的方法基本與HashMap類中的方法差不多,這里就不再進行介紹。

小結

LinkedHashMap 和hashMap 功能基本一樣,都是維護的鍵值對集合,連遍歷 以及方法都類似,唯一的區別在于HashMap 里面的元素是根據hash值來決定存放位置的,是無序的,而LinkedHashMap 維護的是一個按順序存放的雙向鏈表,是有序的。

因此,記住,他們的區別在于:LinkedHashMap是“數組和雙向鏈表”的結合體,而HashMap是“數組和單向鏈表”的結合體就夠了。

總結

以上是生活随笔為你收集整理的容器源码解析之LinkedHashMap(九)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。