国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

目錄
使用簡介
初始化
HashMap#put()
HashMap#resize()
HashMap#get()
首頁 Java java教程 帶你們了解Java8 HashMap源碼解析的步驟

帶你們了解Java8 HashMap源碼解析的步驟

Sep 13, 2018 pm 01:56 PM
hashmap 源碼解析

用過java的朋友或多或少對HashMap有一些不了解的地方,文章內(nèi)容很緊湊,希望大家堅持學(xué)習(xí)哦

前言

Java7中的HashMap和Java8中的HashMap不太一樣,Java7中的HashMap主要是由數(shù)組 鏈表組成的,而Java8中的HashMap是由數(shù)組 鏈表 紅黑樹組成的,當(dāng)鏈表的長度超過8個時,就會轉(zhuǎn)為紅黑樹,降低查找時的時間復(fù)雜度,從而提高效率。這里主要分析的是Java8中的HashMap。

使用簡介

在日常開發(fā)中,我們在使用HashMap的時候,有以下兩種初始化方式:
? ?1、通過new HashMap()不指定初始值大?。?br> ? ?2、通過new HashMap<>(int initialCapacity)指定初始值大小。

初始化

//  數(shù)組的默認初始容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//  最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//  默認負載因子(用來控制閾值和容量,當(dāng)數(shù)組已經(jīng)存放的容量超過了閾值時,容量會擴大一倍
//  閾值 = 最大容量 * 負載因子)
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//  默認鏈表的最大長度(當(dāng)鏈表的長度超過8時會被轉(zhuǎn)換為紅黑樹)
static final int TREEIFY_THRESHOLD = 8;
//  使用第一種初始化方式時,默認初始化容量是2的4次
public HashMap() {    
this.loadFactor = DEFAULT_LOAD_FACTOR; 
}
public HashMap(int initialCapacity) {   
     this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
     public HashMap(int initialCapacity, float loadFactor) {   
      //  不能小于0
    if (initialCapacity < 0) 
             throw new IllegalArgumentException("Illegal initial capacity: initialCapacity); 
                //  超過2的30次方,則最大容量為2的30次方
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;  
                  if (loadFactor <= 0 || Float.isNaN(loadFactor))   
                       throw new IllegalArgumentException("Illegal load factor: loadFactor);
                           this.loadFactor = loadFactor;   
                 //  計算閾值(在第一次put值的時候,會在resize()方法中重新計算)
    this.threshold = tableSizeFor(initialCapacity);
}

HashMap#put()

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

hash()方法主要是對存入的key值進行判斷,如果為null,則返回0;不為null,則返回key的hash值與hash值無符號右移16位以后的值進行按位異或的結(jié)果(有點繞口)。由此可以看出HashMap的key值可以為null。

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;   
     //  第一次put值時,會初始化當(dāng)前數(shù)組長度,如果沒有指定,則默認為16
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;  
          //  找到在數(shù)組中對應(yīng)的下標(biāo),如果該位置沒有值,那么直接初始化一個Node放在此位置
             // 由&運算可以確保(n - 1) & hash一定是小于數(shù)組容量
        tab[i] = newNode(hash, key, value, null);  
          else {
        Node<K,V> e; K k;        
        //  如果key值已經(jīng)存在,則取出這個節(jié)點
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;       
             //  如果當(dāng)前key值的節(jié)點是紅黑樹,則調(diào)用紅黑樹的插值算法
        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);        
                   //  如果鏈表的長度大于等于8,則將鏈表轉(zhuǎn)換為紅黑樹
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);       
                                 break;
                }               
                 //  如果在鏈表的節(jié)點中存在相同的key,則結(jié)束循環(huán)
                if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))                    break;
                p = e;
            }
        }        //  如果存在相同的key值,則重新賦值,并且返回舊值
        if (e != null) { 
        // existing mapping for key
            V oldValue = e.value;           
             //  由源碼可知onlyIfAbsent默認false 
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);  
          return oldValue;
        }
    }
    ++modCount;   
     //  如果數(shù)組已經(jīng)容納的長度超過了設(shè)定的閾值,則會對該數(shù)組進行擴容,每次擴容是之前長度的兩倍
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);   
     //  每一個不同的key值第一次put的時候返回null。
    return null;
}

HashMap#resize()

resize()方法主要作用是初始化數(shù)組或?qū)?shù)組進行擴容計算。

    final Node<K,V>[] resize() {    
       //  備份原始數(shù)組
    Node<K,V>[] oldTab = table;    
    //  第一次put值的時候為0
    int oldCap = (oldTab == null) ? 0 : oldTab.length;   
     //  如果沒有指定初始值大小,第一次put值的時候閾值為0
    int oldThr = threshold;    int newCap, newThr = 0;   
     //  如果數(shù)組不為null且長度不為0,則會
    if (oldCap > 0) {       
     //  如果長度大于等2的30次方,則默認閾值為int的最大值(即2的31次方減1)
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;            
            return oldTab;
        }       
         //  如果將數(shù)組長度擴大一倍后的值小于2的30次方并且數(shù)組之前的長度大于等于2的4次方,則將閾值擴大
          //  一倍,否則閾值會在下面的if (newThr == 0)中進行賦值
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)     
               //  將閾值擴大一倍
            newThr = oldThr << 1; // double threshold
          }   
     //  如果使用new HashMap(int initialCapacity)初始化,則第一次put值時會進入這里
    else if (oldThr > 0) 
        initial capacity was placed in threshold
        newCap = oldThr;  
          //  如果使用new HashMap()初始化,則第一次put值時會進入這里
    else {             
          zero initial threshold signifies using defaults
        //  默認數(shù)組大小是2的4次方
        newCap = DEFAULT_INITIAL_CAPACITY;        
        //  默認負載因子是0.75,即默認閾值為12
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }   
     //  只有以下兩種情況會進入到if判斷中:
      //  1、在使用new HashMap(int initialCapacity)初始化,并且第一次put值的時候
      //   2、對數(shù)組進行擴容且數(shù)組的原始長度小于2的4次方的時候
    if (newThr == 0) {       
     //  根據(jù)指定的數(shù)組大小和負載因子乘積得到閾值
        float ft = (float)newCap * loadFactor;       
      //  如果數(shù)組大小和閾值都小于2的20次方,則確定閾值
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                    (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;   
     @SuppressWarnings({"rawtypes","unchecked"})   
      //  用新的數(shù)組大小初始化新的數(shù)組
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;   
     //  如果是第一次初始化,則直接返回newTab。如果不是則會進行數(shù)據(jù)的遷移操作
    if (oldTab != null) {  
          //  遍歷數(shù)組
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;            
            if ((e = oldTab[j]) != null) {         
                   //  將已經(jīng)被取出的位置置空
                oldTab[j] = null;       
                         //  如果數(shù)組該位置只是單個節(jié)點,那么直接賦值
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;   
                                 //  如果數(shù)組該位置是紅黑樹
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);    
                                //  如果數(shù)組該位置是鏈表,保證原始的循序進行遷移
                else { 
                      preserve order
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;                        
                        if ((e.hash & oldCap) == 0) {                            
                        if (loTail == null)
                                loHead = e;                            
                                else
                                loTail.next = e;
                            loTail = e;
                        }                        
                        else {                            
                        if (hiTail == null)
                                hiHead = e;                            
                                else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);                    
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }                    
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }    return newTab;
}

由resize()方法可以看出,負載因子決定了數(shù)組的容量和使用程度。
負載因子越大則數(shù)組的填裝密度越高,也就是能容納更多的元素。但是由于數(shù)組插入或刪除元素的時間復(fù)雜度O(n),所以索引的效率會變低。
但是,負載因子越小則數(shù)組中的填裝密度越稀疏,此時會空間的浪費,但是此時索引效率高(用空間換取時間)。

HashMap#get()

相較于put方法,get方法就顯得尤為簡單,因為不再需要關(guān)心擴容問題,只需要處理數(shù)據(jù)的獲取。

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

final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;    //  首先判斷數(shù)組不為null以及長度大于0并且對應(yīng)位置節(jié)點不為null
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {        //  判斷第一個節(jié)點是否滿足條件
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))            return first;        //  如果節(jié)點的下一個節(jié)點不為null
        if ((e = first.next) != null) {            //  判斷該節(jié)點是否為紅黑樹
            if (first instanceof TreeNode)                return ((TreeNode<K,V>)first).getTreeNode(hash, key);            //  遍歷鏈表,判斷是否有滿足條件的節(jié)點存在
            do {                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))                    return e;
            } while ((e = e.next) != null);
        }
    }    return null;
}

相關(guān)推薦:

HashMap源碼剖析

詳解Java集合框架HashSet和HashMap源碼剖析(圖)

以上是帶你們了解Java8 HashMap源碼解析的步驟的詳細內(nèi)容。更多信息請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本站聲明
本文內(nèi)容由網(wǎng)友自發(fā)貢獻,版權(quán)歸原作者所有,本站不承擔(dān)相應(yīng)法律責(zé)任。如您發(fā)現(xiàn)有涉嫌抄襲侵權(quán)的內(nèi)容,請聯(lián)系admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費脫衣服圖片

Undresser.AI Undress

Undresser.AI Undress

人工智能驅(qū)動的應(yīng)用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用于從照片中去除衣服的在線人工智能工具。

Clothoff.io

Clothoff.io

AI脫衣機

Video Face Swap

Video Face Swap

使用我們完全免費的人工智能換臉工具輕松在任何視頻中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的代碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

功能強大的PHP集成開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級代碼編輯軟件(SublimeText3)

熱門話題

Laravel 教程
1601
29
PHP教程
1502
276
hashmap的擴容機制是什么 hashmap的擴容機制是什么 Mar 15, 2023 pm 03:39 PM

hashmap的擴容機制是:重新計算容量,用一個新的數(shù)組替換原來的數(shù)組。重新計算原數(shù)組的所有數(shù)據(jù)并插入一個新數(shù)組,然后指向新數(shù)組;如果數(shù)組在容量擴展前已達到最大值,則直接將閾值設(shè)置為最大整數(shù)返回。

基于Java HashMap,如何解決插入重復(fù)的Key值問題 基于Java HashMap,如何解決插入重復(fù)的Key值問題 May 09, 2023 am 10:52 AM

javaHashMap插入重復(fù)Key值要在HashMap中插入重復(fù)的值,首先需要弄清楚HashMap里面是怎么存放元素的。put方法Map里面存放的每一個元素都是key-value這樣的鍵值對,而且都是通過put方法進行添加的,而且相同的key在Map中只會有一個與之關(guān)聯(lián)的value存在。put方法在Map中的定義如下。Vput(Kkey,Vvalue);put()方法實現(xiàn):首先hash(key)得到key的hashcode(),hashmap根據(jù)獲得的hashcode找到要插入的位置所在的鏈,

如何使用HashMap類的put()方法將鍵值對插入到HashMap中 如何使用HashMap類的put()方法將鍵值對插入到HashMap中 Jul 26, 2023 pm 11:53 PM

如何使用HashMap類的put()方法將鍵值對插入到HashMap中HashMap是Java集合框架中的一個非常重要的類,它提供了一種存儲鍵值對的方式。在實際開發(fā)中,我們經(jīng)常需要向HashMap中插入鍵值對,通過使用HashMap類的put()方法可以很輕松地實現(xiàn)這一目標(biāo)。HashMap的put()方法的簽名如下:Vput(Kkey,Vvalue)

Java文檔解讀:HashMap類的containsKey()方法用法詳解 Java文檔解讀:HashMap類的containsKey()方法用法詳解 Nov 04, 2023 am 08:12 AM

Java文檔解讀:HashMap類的containsKey()方法用法詳解,需要具體代碼示例引言:HashMap是Java中常用的一種數(shù)據(jù)結(jié)構(gòu),它提供了高效的存儲和查找功能。其中的containsKey()方法用于判斷HashMap中是否包含指定的鍵。本文將詳細解讀HashMap類的containsKey()方法的使用方式,并提供具體的代碼示例。一、cont

java中LinkedHashMap和HashMap區(qū)別是什么 java中LinkedHashMap和HashMap區(qū)別是什么 May 02, 2023 am 08:31 AM

1、說明Map基本上可以使用HashMap,但是HashMap有一個問題,那就是迭代HashMap的順序不是HashMap放置的順序,就是無序。HashMap的這個缺點往往會帶來麻煩,因為有些場景我們期待一個有序的Map,這就是LinkedHashMap。2、區(qū)別實例publicstaticvoidmain(String[]args){Mapmap=newLinkedHashMap();map.put("apple","蘋果");map.put("

Java使用HashMap類的putAll()函數(shù)將一個Map添加到另一個Map中 Java使用HashMap類的putAll()函數(shù)將一個Map添加到另一個Map中 Jul 24, 2023 am 09:36 AM

Java使用HashMap類的putAll()函數(shù)將一個Map添加到另一個Map中Map是Java中常用的數(shù)據(jù)結(jié)構(gòu),用來表示鍵值對的集合。在Java的集合框架中,HashMap是一個常用的實現(xiàn)類。它提供了putAll()函數(shù),用于將一個Map添加到另一個Map中,方便實現(xiàn)數(shù)據(jù)的合并和拷貝。本文將介紹putAll()函數(shù)的使用方法,并提供相應(yīng)的代碼示例。首先,

Java Map 性能優(yōu)化揭秘:讓你的數(shù)據(jù)操作更快速、更高效 Java Map 性能優(yōu)化揭秘:讓你的數(shù)據(jù)操作更快速、更高效 Feb 20, 2024 am 08:31 AM

JavaMap是Java標(biāo)準(zhǔn)庫中常用的數(shù)據(jù)結(jié)構(gòu),它以鍵值對的形式存儲數(shù)據(jù)。Map的性能對于應(yīng)用程序的運行效率至關(guān)重要,如果Map的性能不佳,可能會導(dǎo)致應(yīng)用程序運行緩慢,甚至崩潰。1.選擇合適的Map實現(xiàn)Java提供了多種Map實現(xiàn),包括HashMap、TreeMap和LinkedHashMap。每種Map實現(xiàn)都有其各自的優(yōu)缺點,在選擇Map實現(xiàn)時,需要根據(jù)應(yīng)用程序的具體需求來選擇合適的實現(xiàn)。HashMap:HashMap是最常用的Map實現(xiàn),它使用哈希表來存儲數(shù)據(jù),具有較快的插入、刪除和查找速度

Java單例模式怎么利用HashMap實現(xiàn)緩存數(shù)據(jù) Java單例模式怎么利用HashMap實現(xiàn)緩存數(shù)據(jù) May 13, 2023 am 09:43 AM

一、單例模式是什么?單例模式是一種對象創(chuàng)建模式,它用于產(chǎn)生一個對象的具體實例,它可以確保系統(tǒng)中一個類只產(chǎn)生一個實例。Java里面實現(xiàn)的單例是一個虛擬機的范圍,因為裝載類的功能是虛擬機的,所以一個虛擬機在通過自己的ClassLoad裝載實現(xiàn)單例類的時候就會創(chuàng)建一個類的實例。在Java語言中,這樣的行為能帶來兩大好處:1.對于頻繁使用的對象,可以省略創(chuàng)建對象所花費的時間,這對于那些重量級對象而言,是非??捎^的一筆系統(tǒng)開銷;2.由于new操作的次數(shù)減少,因而對系統(tǒng)內(nèi)存的使用頻率也會降低,這將減輕GC壓

See all articles