真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java中LinkedHashMap的作用是什么

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)Java中LinkedHashMap的作用是什么,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

創(chuàng)新互聯(lián)公司專業(yè)提供成都移動(dòng)云計(jì)算中心服務(wù),為用戶提供五星數(shù)據(jù)中心、電信、雙線接入解決方案,用戶可自行在線購(gòu)買成都移動(dòng)云計(jì)算中心服務(wù),并享受7*24小時(shí)金牌售后服務(wù)。

put() 方法的講解。

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    HashMap.Node[] tab; HashMap.Node p; int n, i;
    // ①、數(shù)組 table 為 null 時(shí),調(diào)用 resize 方法創(chuàng)建默認(rèn)大小的數(shù)組
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // ②、計(jì)算下標(biāo),如果該位置上沒(méi)有值,則填充
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
}
 

這個(gè)公式 i = (n - 1) & hash 計(jì)算后的值并不是按照 0、1、2、3、4、5 這樣有序的下標(biāo)將鍵值對(duì)插入到數(shù)組當(dāng)中的,而是有一定的隨機(jī)性。

那 LinkedHashMap 就是為這個(gè)需求應(yīng)運(yùn)而生的。LinkedHashMap 繼承了 HashMap,所以 HashMap 有的關(guān)于鍵值對(duì)的功能,它也有了。

public class LinkedHashMap
    extends HashMap
    implements Map{}
 

此外,LinkedHashMap 內(nèi)部又追加了雙向鏈表,來(lái)維護(hù)元素的插入順序。注意下面代碼中的 before 和 after,它倆就是用來(lái)維護(hù)當(dāng)前元素的前一個(gè)元素和后一個(gè)元素的順序的。

static class Entry extends HashMap.Node {
    LinkedHashMap.Entry before, after;
    Entry(int hash, K key, V value, HashMap.Node next) {
        super(hash, key, value, next);
    }
}
 

關(guān)于雙向鏈表,同學(xué)們可以回頭看一遍我寫的 LinkedList 那篇文章,會(huì)對(duì)理解本篇的 LinkedHashMap 有很大的幫助。

在繼續(xù)下面的內(nèi)容之前,我先貼一張圖片,給大家增添一點(diǎn)樂(lè)趣——看我這心操的。UUID 那篇文章的標(biāo)題里用了“可笑”和“你”,結(jié)果就看到了下面這么樂(lè)呵的留言。

Java中LinkedHashMap的作用是什么  

(到底是知道還是不知道,我搞不清楚了。。。)那 LinkedHashMap 這篇也用了“你”和“可笑”,不知道到時(shí)候會(huì)不會(huì)有人繼續(xù)對(duì)號(hào)入座啊,想想就覺(jué)得特別歡樂(lè)。

 

01、插入順序

在 HashMap 那篇文章里,我有講解到一點(diǎn),不知道同學(xué)們記不記得,就是 null 會(huì)插入到 HashMap 的第一位。

Map hashMap = new HashMap<>();
hashMap.put("沉", "沉默王二");
hashMap.put("默", "沉默王二");
hashMap.put("王", "沉默王二");
hashMap.put("二", "沉默王二");
hashMap.put(null, null);

for (String key : hashMap.keySet()) {
    System.out.println(key + " : " + hashMap.get(key));
}
 

輸出的結(jié)果是:

null : null
默 : 沉默王二
沉 : 沉默王二
王 : 沉默王二
二 : 沉默王二
 

雖然 null 最后一位 put 進(jìn)去的,但在遍歷輸出的時(shí)候,跑到了第一位。

那再來(lái)對(duì)比看一下 LinkedHashMap。

Map linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("沉", "沉默王二");
linkedHashMap.put("默", "沉默王二");
linkedHashMap.put("王", "沉默王二");
linkedHashMap.put("二", "沉默王二");
linkedHashMap.put(null, null);

for (String key : linkedHashMap.keySet()) {
    System.out.println(key + " : " + linkedHashMap.get(key));
}
 

輸出結(jié)果是:

沉 : 沉默王二
默 : 沉默王二
王 : 沉默王二
二 : 沉默王二
null : null
 

null 在最后一位插入,在最后一位輸出。

輸出結(jié)果可以再次證明,HashMap 是無(wú)序的,LinkedHashMap 是可以維持插入順序的。

那 LinkedHashMap 是如何做到這一點(diǎn)呢?我相信同學(xué)們和我一樣,非常希望知道原因。

要想搞清楚,就需要深入研究一下 LinkedHashMap 的源碼。LinkedHashMap 并未重寫 HashMap 的 put() 方法,而是重寫了 put() 方法需要調(diào)用的內(nèi)部方法 newNode()

HashMap.Node newNode(int hash, K key, V value, HashMap.Node e) {
    LinkedHashMap.Entry p =
            new LinkedHashMap.Entry<>(hash, key, value, e);
    linkNodeLast(p);
    return p;
}
 

前面說(shuō)了,LinkedHashMap.Entry 繼承了 HashMap.Node,并且追加了兩個(gè)字段 before 和 after。

那,緊接著來(lái)看看 linkNodeLast() 方法:

private void linkNodeLast(LinkedHashMap.Entry p) {
    LinkedHashMap.Entry last = tail;
    tail = p;
    if (last == null)
        head = p;
    else {
        p.before = last;
        last.after = p;
    }
}
 

看到了吧,LinkedHashMap 在添加第一個(gè)元素的時(shí)候,會(huì)把 head 賦值為第一個(gè)元素,等到第二個(gè)元素添加進(jìn)來(lái)的時(shí)候,會(huì)把第二個(gè)元素的 before 賦值為第一個(gè)元素,第一個(gè)元素的 afer 賦值為第二個(gè)元素。

這就保證了鍵值對(duì)是按照插入順序排列的,明白了吧?

注:我用到的 JDK 版本為 14

 

02、訪問(wèn)順序

LinkedHashMap 不僅能夠維持插入順序,還能夠維持訪問(wèn)順序。訪問(wèn)包括調(diào)用 get() 方法、remove() 方法和 put() 方法。

要維護(hù)訪問(wèn)順序,需要我們?cè)诼暶?LinkedHashMap 的時(shí)候指定三個(gè)參數(shù)。

LinkedHashMap map = new LinkedHashMap<>(16, .75f, true);
 

第一個(gè)參數(shù)和第二個(gè)參數(shù),看過(guò) HashMap 的同學(xué)們應(yīng)該很熟悉了,指的是初始容量和負(fù)載因子。

第三個(gè)參數(shù)如果為 true 的話,就表示 LinkedHashMap 要維護(hù)訪問(wèn)順序;否則,維護(hù)插入順序。默認(rèn)是 false。

Map linkedHashMap = new LinkedHashMap<>(16, .75f, true);
linkedHashMap.put("沉", "沉默王二");
linkedHashMap.put("默", "沉默王二");
linkedHashMap.put("王", "沉默王二");
linkedHashMap.put("二", "沉默王二");

System.out.println(linkedHashMap);

linkedHashMap.get("默");
System.out.println(linkedHashMap);

linkedHashMap.get("王");
System.out.println(linkedHashMap);
 

輸出的結(jié)果如下所示:

{沉=沉默王二, 默=沉默王二, 王=沉默王二, 二=沉默王二}
{沉=沉默王二, 王=沉默王二, 二=沉默王二, 默=沉默王二}
{沉=沉默王二, 二=沉默王二, 默=沉默王二, 王=沉默王二}
 

當(dāng)我們使用 get() 方法訪問(wèn)鍵位“默”的元素后,輸出結(jié)果中,默=沉默王二 在最后;當(dāng)我們?cè)L問(wèn)鍵位“王”的元素后,輸出結(jié)果中,王=沉默王二 在最后,默=沉默王二 在倒數(shù)第二位。

也就是說(shuō),最不經(jīng)常訪問(wèn)的放在頭部,這就有意思了。有意思在哪呢?

我們可以使用 LinkedHashMap 來(lái)實(shí)現(xiàn) LRU 緩存,LRU 是 Least Recently Used 的縮寫,即最近最少使用,是一種常用的頁(yè)面置換算法,選擇最近最久未使用的頁(yè)面予以淘汰。

public class MyLinkedHashMap extends LinkedHashMap {

    private static final int MAX_ENTRIES = 5;

    public MyLinkedHashMap(
            int initialCapacity, float loadFactor, boolean accessOrder) {
        super(initialCapacity, loadFactor, accessOrder);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > MAX_ENTRIES;
    }

}
 

MyLinkedHashMap 是一個(gè)自定義類,它繼承了 LinkedHashMap,并且重寫了 removeEldestEntry() 方法——使 Map 最多可容納 5 個(gè)元素,超出后就淘汰。

我們來(lái)測(cè)試一下。

MyLinkedHashMap map = new MyLinkedHashMap<>(16,0.75f,true);
map.put("沉", "沉默王二");
map.put("默", "沉默王二");
map.put("王", "沉默王二");
map.put("二", "沉默王二");
map.put("一枚有趣的程序員", "一枚有趣的程序員");

System.out.println(map);

map.put("一枚有顏值的程序員", "一枚有顏值的程序員");
System.out.println(map);

map.put("一枚有才華的程序員","一枚有才華的程序員");
System.out.println(map);
 

輸出結(jié)果如下所示:

{沉=沉默王二, 默=沉默王二, 王=沉默王二, 二=沉默王二, 一枚有趣的程序員=一枚有趣的程序員}
{默=沉默王二, 王=沉默王二, 二=沉默王二, 一枚有趣的程序員=一枚有趣的程序員, 一枚有顏值的程序員=一枚有顏值的程序員}
{王=沉默王二, 二=沉默王二, 一枚有趣的程序員=一枚有趣的程序員, 一枚有顏值的程序員=一枚有顏值的程序員, 一枚有才華的程序員=一枚有才華的程序員}
 

沉=沉默王二默=沉默王二 依次被淘汰出局。

假如在 put “一枚有才華的程序員”之前 get 了鍵位為“默”的元素:

MyLinkedHashMap map = new MyLinkedHashMap<>(16,0.75f,true);
map.put("沉", "沉默王二");
map.put("默", "沉默王二");
map.put("王", "沉默王二");
map.put("二", "沉默王二");
map.put("一枚有趣的程序員", "一枚有趣的程序員");

System.out.println(map);

map.put("一枚有顏值的程序員", "一枚有顏值的程序員");
System.out.println(map);

map.get("默");
map.put("一枚有才華的程序員","一枚有才華的程序員");
System.out.println(map);
 

那輸出結(jié)果就變了,對(duì)吧?

{沉=沉默王二, 默=沉默王二, 王=沉默王二, 二=沉默王二, 一枚有趣的程序員=一枚有趣的程序員}
{默=沉默王二, 王=沉默王二, 二=沉默王二, 一枚有趣的程序員=一枚有趣的程序員, 一枚有顏值的程序員=一枚有顏值的程序員}
{二=沉默王二, 一枚有趣的程序員=一枚有趣的程序員, 一枚有顏值的程序員=一枚有顏值的程序員, 默=沉默王二, 一枚有才華的程序員=一枚有才華的程序員}
 

沉=沉默王二王=沉默王二 被淘汰出局了。

那 LinkedHashMap 是如何來(lái)維持訪問(wèn)順序呢?同學(xué)們感興趣的話,可以研究一下下面這三個(gè)方法。

void afterNodeAccess(Node p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node p) { }
 

afterNodeAccess() 會(huì)在調(diào)用 get() 方法的時(shí)候被調(diào)用,afterNodeInsertion() 會(huì)在調(diào)用 put() 方法的時(shí)候被調(diào)用,afterNodeRemoval() 會(huì)在調(diào)用 remove() 方法的時(shí)候被調(diào)用。

我來(lái)以 afterNodeAccess() 為例來(lái)講解一下。

void afterNodeAccess(HashMap.Node e) { // move node to last
    LinkedHashMap.Entry last;
    if (accessOrder && (last = tail) != e) {
        LinkedHashMap.Entry p =
                (LinkedHashMap.Entry)e, b = p.before, a = p.after;
        p.after = null;
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a != null)
            a.before = b;
        else
            last = b;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
        tail = p;
        ++modCount;
    }
}
 

哪個(gè)元素被 get 就把哪個(gè)元素放在最后。了解了吧?

那同學(xué)們可能還想知道,為什么 LinkedHashMap 能實(shí)現(xiàn)  LRU 緩存,把最不經(jīng)常訪問(wèn)的那個(gè)元素淘汰?

在插入元素的時(shí)候,需要調(diào)用 put() 方法,該方法最后會(huì)調(diào)用 afterNodeInsertion() 方法,這個(gè)方法被 LinkedHashMap 重寫了。

void afterNodeInsertion(boolean evict) { // possibly remove eldest
    LinkedHashMap.Entry first;
    if (evict && (first = head) != null && removeEldestEntry(first)) {
        K key = first.key;
        removeNode(hash(key), key, null, false, true);
    }
}
 

removeEldestEntry() 方法會(huì)判斷第一個(gè)元素是否超出了可容納的最大范圍,如果超出,那就會(huì)調(diào)用 removeNode() 方法對(duì)最不經(jīng)常訪問(wèn)的那個(gè)元素進(jìn)行刪除。

上述就是小編為大家分享的Java中LinkedHashMap的作用是什么了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


文章題目:Java中LinkedHashMap的作用是什么
當(dāng)前網(wǎng)址:http://weahome.cn/article/ghhiij.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部