成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

HashMap實(shí)現(xiàn)原理

pf_miles / 1455人閱讀

摘要:當(dāng)中包括的元素變得比較少時(shí),為了存儲(chǔ)空間的占用,也會(huì)轉(zhuǎn)換為節(jié)點(diǎn)單向鏈表的方式實(shí)現(xiàn),它們之間可以互相轉(zhuǎn)換的。

hashMap是通過(guò)數(shù)組存儲(chǔ)所有的數(shù)據(jù),每個(gè)元素所存放數(shù)組的下標(biāo),是根據(jù)該存儲(chǔ)元素的key的Hash值與該數(shù)組的長(zhǎng)度減去1做與運(yùn)算,如下所示:
index = (length_of_array - 1) & hash_of_the_key;
數(shù)組中存放元素的數(shù)據(jù)結(jié)構(gòu)使用了Node和TreeNode兩種數(shù)據(jù)結(jié)構(gòu),在單個(gè)Hash值對(duì)應(yīng)的存儲(chǔ)元素小于8個(gè)時(shí),默認(rèn)值為Node的單向鏈表形式存儲(chǔ),當(dāng)單個(gè)Hash值存儲(chǔ)的元素大于8個(gè)時(shí),其會(huì)使用TreeNode的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)。
因?yàn)樵趩蝹€(gè)Hash值對(duì)應(yīng)的元素小于等于8個(gè)時(shí),其查詢(xún)時(shí)間最差為O(8),但是當(dāng)單個(gè)Hash值對(duì)應(yīng)的元素大于8個(gè)時(shí),再通過(guò)Node的單向鏈表的方式進(jìn)行查詢(xún),速度上就會(huì)變得更慢了;這個(gè)時(shí)候HashMap就會(huì)將Node的普通節(jié)點(diǎn)轉(zhuǎn)為T(mén)reeNode(紅黑樹(shù))進(jìn)行存儲(chǔ),這是由于TreeNode占用的空間大小約為常規(guī)節(jié)點(diǎn)的兩倍,但是其查詢(xún)速度可以得到保證,這個(gè)是通過(guò)空間換時(shí)間了。當(dāng)TreeNode中包括的元素變得比較少時(shí),為了存儲(chǔ)空間的占用,也會(huì)轉(zhuǎn)換為Node節(jié)點(diǎn)單向鏈表的方式實(shí)現(xiàn),它們之間可以互相轉(zhuǎn)換的。
Node:

 static class Node implements Map.Entry {
    final int hash;
    final K key;
    V value;
    Node next;

    Node(int hash, K key, V value, Node next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
    ......

}

可以看到每個(gè)Node中包括了4個(gè)屬性,分別為:
hash值:當(dāng)前Node的Hash值
key:當(dāng)前Node的key
value:當(dāng)前Node的value
next:表示指向下一個(gè)Node的指針,相同hash值的Node,通過(guò)next進(jìn)行遍歷查找
TreeNode:

static final class TreeNode extends LinkedHashMap.Entry {
    TreeNode parent;  // red-black tree links
    TreeNode left;
    TreeNode right;
    TreeNode prev;    // needed to unlink next upon deletion
    boolean red;
    TreeNode(int hash, K key, V val, Node next) {
        super(hash, key, val, next);
    }
    ......

}
可以看到TreeNode使用的是紅黑樹(shù)(Red Black Tree)的數(shù)據(jù)結(jié)構(gòu),紅黑樹(shù)是一種自平衡二叉查找樹(shù),在進(jìn)行插入和刪除操作時(shí)通過(guò)特定操作保持二叉查找樹(shù)的平衡,從而獲得較高的查找性能,即使在最壞情況運(yùn)行時(shí)間也是非常良好的,并且在實(shí)踐中是非常高效的,它可以在O(log n)時(shí)間內(nèi)做查找、插入和刪除等操作,這里的n 是樹(shù)中元素的數(shù)目。
以下是一張關(guān)于HashMap存儲(chǔ)結(jié)構(gòu)的示意圖:

1.寫(xiě)入數(shù)據(jù)
其方法如下:

//寫(xiě)入數(shù)據(jù)

public V put(K key, V value) {
//首先根據(jù)hash方法,獲取對(duì)應(yīng)key的hash值,計(jì)算方法見(jiàn)后面
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
Node[] tab; Node p; int n, i;
//判斷用戶(hù)存放元素的數(shù)組是否為空 
if ((tab = table) == null || (n = tab.length) == 0)
    //為空則進(jìn)行初使化,并將初使化后的數(shù)組賦值給變量tab,數(shù)組的長(zhǎng)值賦值給變量n
    n = (tab = resize()).length;
//判斷根據(jù)hash值與數(shù)組長(zhǎng)度減1求與得到的下標(biāo),
//從數(shù)組中獲取元素并將其賦值給變量p(后續(xù)該變量p可以繼續(xù)使用),并判斷該元素是否存在
if ((p = tab[i = (n - 1) & hash]) == null)
    //如果不存在則創(chuàng)建一個(gè)新的節(jié)點(diǎn),并將其放到數(shù)組對(duì)應(yīng)的下標(biāo)中
    tab[i] = newNode(hash, key, value, null);
else {//根據(jù)數(shù)組的下標(biāo)取到了元素,并且該元素p且不為空,下面要判斷p元素的類(lèi)型是Node還是TreeNode
    Node e; K k;
    //判斷該數(shù)組對(duì)應(yīng)下標(biāo)取到的第一值是不是與正在存入值的hash值相同、
    //key相等(可能是對(duì)象,也可能是字符串),如果相等,則將取第一個(gè)值賦值給變量e
    if (p.hash == hash &&
        ((k = p.key) == key || (key != null && key.equals(k))))
        e = p;
    //判斷取的對(duì)象是不是TreeNode,如果是則執(zhí)行TreeNode的put方法
    else if (p instanceof TreeNode)
        e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
    else {//是普通的Node節(jié)點(diǎn),
        //根據(jù)next屬性對(duì)元素p執(zhí)行單向鏈表的遍歷
        for (int binCount = 0; ; ++binCount) {
            //如果被遍歷的元素最后的next為空,表示后面沒(méi)有節(jié)點(diǎn)了,則將新節(jié)點(diǎn)與當(dāng)前節(jié)點(diǎn)的next屬性建立關(guān)系
            if ((e = p.next) == null) {
                //做為當(dāng)前節(jié)點(diǎn)的后面的一個(gè)節(jié)點(diǎn)
                p.next = newNode(hash, key, value, null);
                //判斷當(dāng)前節(jié)點(diǎn)的單向鏈接的數(shù)量(8個(gè))是不是已經(jīng)達(dá)到了需要將其轉(zhuǎn)換為T(mén)reeNode了
                if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                    //如果是則將當(dāng)前數(shù)組下標(biāo)對(duì)應(yīng)的元素轉(zhuǎn)換為T(mén)reeNode
                    treeifyBin(tab, hash);
                break;
            }
            //判斷待插入的元素的hash值與key是否與單向鏈表中的某個(gè)元素的hash值與key是相同的,如果是則退出
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                break;
            p = e;
        }
    }
    //判斷是否找到了與待插入元素的hash值與key值都相同的元素
    if (e != null) { // existing mapping for key
        V oldValue = e.value;
        //判斷是否要將舊值替換為新值
        if (!onlyIfAbsent || oldValue == null)
            //滿(mǎn)足于未指定不替換或舊值為空的情況,執(zhí)行將舊值替換為新值
            e.value = value;
        afterNodeAccess(e);
        return oldValue;
    }
}
++modCount;
if (++size > threshold)
    resize();
afterNodeInsertion(evict);
return null;
}

2.讀取數(shù)據(jù)
public V get(Object key) {

    Node e;
    //根據(jù)Key獲取元素
    if ((e = getNode(hash(key), key)) == null)
        return null;
    if (accessOrder)
        afterNodeAccess(e);
    return e.value;
}

final Node getNode(int hash, Object key) {
    Node[] tab; Node first, e; int n; K k;
    //if語(yǔ)句的第一個(gè)判斷條件
    if ((tab = table) != null //將數(shù)組賦值給變量tab,將判斷是否為null
        && (n = tab.length) > 0 //將數(shù)組的長(zhǎng)值賦值給變量n
        && (first = tab[(n - 1) & hash]) != null) {//判斷根據(jù)hash和數(shù)組長(zhǎng)度減1的與運(yùn)算,計(jì)算出來(lái)的的數(shù)組下標(biāo)的第一個(gè)元素是不是為空
        //判斷第一個(gè)元素是否要找的元素,大部份情況下只要hash值太集中,或者元素不是很多,第一個(gè)元素往往都是需要的最終元素
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            //第一個(gè)元素就是要找的元素,因?yàn)閔ash值和key都相等,直接返回
            return first;
        if ((e = first.next) != null) {//如果第一元素不是要找到的元,則判斷其next指向是否還有元素
            //有元素,判斷其是否是TreeNode
            if (first instanceof TreeNode)
                //是TreeNode則根據(jù)TreeNode的方式獲取數(shù)據(jù)
                return ((TreeNode)first).getTreeNode(hash, key);
            do {//是Node單向鏈表,則通過(guò)next循環(huán)匹配,找到就退出,否則直到匹配完最后一個(gè)元素才退出
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    //沒(méi)有找到則返回null
    return null;
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77798.html

相關(guān)文章

  • HashMap實(shí)現(xiàn)原理筆記

    摘要:掌握的實(shí)現(xiàn)原理,已經(jīng)是程序員的基礎(chǔ)操作了。后記這次主要是理解了一下的實(shí)現(xiàn)原理,特別重點(diǎn)寫(xiě)了很多關(guān)于散列函數(shù)的理解,并沒(méi)有按照源碼一行行的去理解。之前看的時(shí)候?qū)ι⒘泻瘮?shù)都是跳過(guò)去的,只知道是用來(lái)計(jì)算鍵的,不知道里面的原理。 序 HashMap是Java中常用的Map接口的實(shí)現(xiàn)類(lèi),因?yàn)樵谌粘9ぷ髦蟹浅nl繁的出現(xiàn),所以在大部分的Java面試中都會(huì)問(wèn)幾個(gè)關(guān)于HashMap的問(wèn)題。掌握HashM...

    _DangJin 評(píng)論0 收藏0
  • HashMap 的工作原理

    摘要:的工作原理是近年來(lái)常見(jiàn)的面試題。讓我們?cè)賮?lái)看看這些問(wèn)題設(shè)計(jì)哪些知識(shí)點(diǎn)的概念中解決碰撞的方法和的應(yīng)用,以及它們?cè)谥械闹匾圆豢勺儗?duì)象的好處多線(xiàn)程的條件競(jìng)爭(zhēng)重新調(diào)整的大小總結(jié)的工作原理基于原理,我們通過(guò)和方法儲(chǔ)存和獲取對(duì)象。 HashMap 的工作原理是近年來(lái)常見(jiàn)的 Java 面試題。幾乎每個(gè) Java 程序員都知道 HashMap,都知道哪里要用 HashMap,知道Hashtable和...

    zhisheng 評(píng)論0 收藏0
  • HashMap 精講原理

    摘要:從結(jié)構(gòu)實(shí)現(xiàn)來(lái)講,是數(shù)組鏈表紅黑樹(shù)增加了紅黑樹(shù)部分實(shí)現(xiàn)的。當(dāng)鏈表長(zhǎng)度大于時(shí),將這個(gè)鏈表轉(zhuǎn)換成紅黑樹(shù),利用紅黑樹(shù)快速增刪改查的特點(diǎn)提高的性能。 原文鏈接 更多教程 本文涉及HashMap的: HashMap的簡(jiǎn)單使用 HashMap的存儲(chǔ)結(jié)構(gòu)原理 HashMap的擴(kuò)容方法原理 HashMap中定位數(shù)據(jù)索引實(shí)現(xiàn) HashMap中put、get方法實(shí)現(xiàn) HashMap的簡(jiǎn)單使用 Ha...

    Lyux 評(píng)論0 收藏0
  • 小馬哥Java項(xiàng)目實(shí)戰(zhàn)訓(xùn)練營(yíng) 極客大學(xué)

    摘要:百度網(wǎng)盤(pán)提取碼一面試題熟練掌握是很關(guān)鍵的,大公司不僅僅要求你會(huì)使用幾個(gè),更多的是要你熟悉源碼實(shí)現(xiàn)原理,甚至要你知道有哪些不足,怎么改進(jìn),還有一些有關(guān)的一些算法,設(shè)計(jì)模式等等。 ??百度網(wǎng)盤(pán)??提取碼:u6C4?一、java面試題熟練掌握java是很關(guān)鍵的,大公司不僅僅要求你會(huì)使用幾個(gè)api,更多的是要你熟悉源碼實(shí)現(xiàn)原理,甚...

    不知名網(wǎng)友 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

pf_miles

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<