欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的HashMap集合源碼詳細解讀

 更新時間:2023年11月15日 10:29:48   作者:緣之妙不可言  
這篇文章主要介紹了Java中的HashMap集合源碼詳細解讀,hash表是一種數(shù)據(jù)結(jié)構(gòu),它擁有驚人的效率,它的時間復(fù)雜度低到接近O(1)這樣的常數(shù)級,需要的朋友可以參考下

什么是Hash?

hash表是一種數(shù)據(jù)結(jié)構(gòu),它擁有驚人的效率,它的時間復(fù)雜度低到接近O(1)這樣的常數(shù)級。

hash表的實現(xiàn)主要是:

1.計算存儲位置的hash函數(shù)。

2.處理哈希沖突的方法。

3.hash的物理存儲。

hash函數(shù)

它的目的是通過一個key選出(映射)一個唯一的存儲地址。

最常見的hash函數(shù):f(key)=a*key+b 這里a,b為常數(shù)(不為0),f(key)就是計算出的哈希值 一般一個hash函數(shù)的設(shè)計好壞,直接影響到效率。

哈希沖突

hash沖突定義:當(dāng)兩個key相同時計算的hash值會一樣,導(dǎo)致沖突。

hash沖突解決部分方法: **開放定地址:**找下一塊地址(另一個hash表(另一個未用過的hash值·),無限多) **再散列函數(shù)法:**一個hash函數(shù)解決不了,就兩個,兩個不行就三個… **鏈地址法(hash桶(此處鏈表)的概念):**數(shù)組+鏈表,將具有相同hash的同義詞按次序放入鏈表,并鏈表存在數(shù)組。鏈表的hash值以數(shù)組hash值開始確定新的hash(不擔(dān)心與其它數(shù)組的hash相同) 還有的就不做介紹了

物理存儲

物理存儲結(jié)構(gòu):順序|鏈?zhǔn)酱鎯?hash表的主干永遠都是一個數(shù)組 一般的hash只需要一個數(shù)組來存儲,一般以數(shù)組下標(biāo)做hash值。 在鏈地址法中需要用到鏈表。

什么是Map?

Map在計算機概念是一個key-value(唯一性)鍵值對

什么是HashMap?

根據(jù)前面的鋪墊, hashMap的存儲主干是一個數(shù)組(源碼中的Node(有些是Entry)對象數(shù)組), Node(Entry)對象包含了Key-Value屬性

hashMap處理hash沖突:java1.8后,使用的是鏈地址法。

數(shù)據(jù)存儲結(jié)構(gòu)如下:

在這里插入圖片描述

在擁有鏈表的情況下,hashMap的查詢效率必然是低一些的,復(fù)雜度提高到o(n) 但1.8利用了紅黑樹數(shù)據(jù)結(jié)構(gòu),又將復(fù)雜度降為了o(Log(n))

HashMap的源碼分析

構(gòu)造函數(shù)(4個)

部分成員變量:

  • transient 關(guān)鍵字:再被修飾后變量序列化將不可見
  • threshold:初始空間(initialCapacity傳入?yún)?shù))
  • loadFactor:負(fù)載因子 modCount:是快速失敗的判斷標(biāo)準(zhǔn)(在迭代時,其他線程訪問Map,并導(dǎo)致其結(jié)構(gòu)改變,會拋異常)
  • table:節(jié)點Set集合(HashMap主干,是個數(shù)組)
  • initialCapacity默認(rèn)為16,loadFactory默認(rèn)為0.75

負(fù)載因子在0.75時效率最好(數(shù)學(xué)統(tǒng)計學(xué)驗證),用于衡量空間利用的方法選擇

hashMap會擴容且容量永遠是2的冪

	合理判斷參數(shù),就結(jié)束構(gòu)造
 	public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }

 	 合理判斷空間大小參數(shù),使用默認(rèn)負(fù)載參數(shù)就結(jié)束構(gòu)造
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

 	 /使用默認(rèn)參數(shù)就結(jié)束構(gòu)造
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

	構(gòu)造一個具有相同的映射關(guān)系與指定Map一個新的HashMap。 HashMap中與默認(rèn)負(fù)載因數(shù)(0.75)和初始容量足以容納在指定的地圖的映射創(chuàng)建
    public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }

發(fā)現(xiàn)此時table變量未分配空間。根據(jù)put函數(shù)源碼發(fā)現(xiàn),table變量在put()分配空間

再看關(guān)鍵對象Node(Entry)

	
	//Map.Entry<K,V>是一個接口
	//Node
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;//哈希值
        final K key;//鍵值
        V value;
        Node<K,V> next;//下一節(jié)點;因為是鏈地址法

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

再看其部分關(guān)鍵函數(shù) hash() : key.hashCode()產(chǎn)生依靠equals()方法,位移異或等操作的哈希值 ,這個根據(jù)版本不同也計算方法不同,但是hashCode()幾乎一直存在 put() : 計算hash值然后對table

	/*單位計算key.hashCode()和差(異或)的散列以降低的較高位。
	 因為表使用功率的兩掩蔽,套散列的,只有在當(dāng)前的掩模將總是碰撞上述位變化。 
	 (其中著名的例子是一組浮動鍵的小桌子控股連續(xù)整數(shù))。
	 所以我們施加向下變換利差較高位的影響。 
	 有速度,實用,和位傳播質(zhì)量之間的權(quán)衡。 
	 由于哈希許多共同的集已合理分配(所以不要蔓延受益),因為我們用樹來處理大型成套碰撞的垃圾箱,
	 我們只是XOR一些最便宜的方式轉(zhuǎn)移位降低系統(tǒng)lossage,
	 以及納入,否則將永遠不會因為表界的指數(shù)計算中使用的最高位的影響。
	 */

	 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
 	public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

    /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    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 {
            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 1st
                            treeifyBin(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 key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        //快速失敗
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

JDK1.8在JDK1.7的基礎(chǔ)上針對增加了紅黑樹來進行優(yōu)化。即當(dāng)鏈表超過8時,鏈表就轉(zhuǎn)換為紅黑樹,利用紅黑樹快速增刪改查的特點提高HashMap的性能,其中會用到紅黑樹的插入、刪除、查找等算法。

看上面的put函數(shù)的putVal()就含有紅黑樹的插入方法, 一開始普通的鏈表不需要紅黑樹。 下列源碼可以看出, 轉(zhuǎn)換紅黑樹條件是 鏈表(是在每次遍歷到數(shù)組時每個單元對于的鏈表,參考上圖)節(jié)點數(shù)大于等于8且數(shù)組長度大于等于64,

詳見方法:treeifyBin();

//判斷處理方法
    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K,V> hd = null, tl = null;
            do {
                TreeNode<K,V> p = replacementTreeNode(e, null);
                if (tl == null)
                    hd = p;
                else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }
//紅黑樹構(gòu)造類
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;//是紅節(jié)點嗎
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
        }
        }

HashMap源碼探究到此為止,以后可以繼續(xù)深入,學(xué)習(xí)紅黑樹的實際應(yīng)用等等

在此附上一張網(wǎng)上Copy過來的圖: 是講1.8版本的HashMap在增加元素后一系列的操作步驟,以及優(yōu)化方式流程圖

在這里插入圖片描述

集合源碼魅力無窮。 踏踏實實讀源碼。

到此這篇關(guān)于Java中的HashMap集合源碼詳細解讀的文章就介紹到這了,更多相關(guān)HashMap集合源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java MAVEN 工程pom配置報錯解決方案

    Java MAVEN 工程pom配置報錯解決方案

    這篇文章主要介紹了Java MAVEN 工程pom配置報錯解決方案,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • 關(guān)于java中基本數(shù)據(jù)類型的數(shù)值范圍

    關(guān)于java中基本數(shù)據(jù)類型的數(shù)值范圍

    這篇文章主要介紹了關(guān)于java中基本數(shù)據(jù)類型的數(shù)值范圍,基本類型,或者叫做內(nèi)置類型,是JAVA中不同于類的特殊類型,它們是我們編程中使用最頻繁的類型,需要的朋友可以參考下
    2023-07-07
  • SpringBoot ResponseEntity標(biāo)識Http響應(yīng)方式

    SpringBoot ResponseEntity標(biāo)識Http響應(yīng)方式

    這篇文章主要介紹了SpringBoot ResponseEntity標(biāo)識Http響應(yīng)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Java獲取時間打印到控制臺代碼實例

    Java獲取時間打印到控制臺代碼實例

    這篇文章主要介紹了Java獲取時間打印到控制臺代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • mybatis輸出SQL格式化方式

    mybatis輸出SQL格式化方式

    這篇文章主要介紹了mybatis輸出SQL格式化方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 命令提示符編譯java的方法(必看篇)

    命令提示符編譯java的方法(必看篇)

    下面小編就為大家?guī)硪黄钐崾痉幾gjava的方法(必看篇)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • Java詳解表格的創(chuàng)建與使用流程

    Java詳解表格的創(chuàng)建與使用流程

    這篇文章主要介紹了怎么用Java來創(chuàng)建和使用表格,表格是我們經(jīng)常要用的工具,但是你有想過自己怎么去實現(xiàn)它嗎,感興趣的朋友跟隨文章往下看看吧
    2022-04-04
  • Java壓縮/解壓文件的實現(xiàn)代碼

    Java壓縮/解壓文件的實現(xiàn)代碼

    本文通過實例代碼給大家分享了Java壓縮/解壓文件的方法,需要的朋友參考下吧
    2017-09-09
  • SpringBoot集成POI實現(xiàn)Excel導(dǎo)入導(dǎo)出的示例詳解

    SpringBoot集成POI實現(xiàn)Excel導(dǎo)入導(dǎo)出的示例詳解

    Apache?POI?是用Java編寫的免費開源的跨平臺的?Java?API,Apache?POI提供API給Java程序?qū)icrosoft?Office格式檔案讀和寫的功能。本文主要介紹通過SpringBoot集成POI工具實現(xiàn)Excel的導(dǎo)入和導(dǎo)出功能,需要的可以參考一下
    2022-07-07
  • SpringBoot項目中使用Mockito的示例代碼

    SpringBoot項目中使用Mockito的示例代碼

    這篇文章主要介紹了SpringBoot項目中使用Mockito的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10

最新評論