最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

談?wù)凥ashmap的容量為什么是2的冪次問(wèn)題

 更新時(shí)間:2020年09月23日 16:10:27   作者:wanghuan220323  
這篇文章主要介紹了談?wù)凥ashmap的容量為什么是2的冪次問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧

做為面試常考的問(wèn)題之一,每次都答的模模糊糊,有必要了解一下,首先來(lái)看一下hashmap的put方法的源碼

public V put(K key, V value) {
  if (key == null)    
   return putForNullKey(value);  //將空key的Entry加入到table[0]中
  int hash = hash(key.hashCode());  //計(jì)算key.hashcode()的hash值,hash函數(shù)由hashmap自己實(shí)現(xiàn)
  int i = indexFor(hash, table.length);//獲取將要存放的數(shù)組下標(biāo)
  /*
   * for中的代碼用于:當(dāng)hash值相同且key相同的情況下,使用新值覆蓋舊值(其實(shí)就是修改功能)
   */
  for (Entry<K, V> e = table[i]; e != null; e = e.next) {//注意:for循環(huán)在第一次執(zhí)行時(shí)就會(huì)先判斷條件
   Object k;
   //hash值相同且key相同的情況下,使用新值覆蓋舊值
   if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
    V oldValue = e.value;
    e.value = value;
    //e.recordAccess(this);
    return oldValue;//返回舊值
   }
  }
  
  modCount++;
  addEntry(hash, key, value, i);//增加一個(gè)新的Entry到table[i]
  return null;//如果沒(méi)有與傳入的key相等的Entry,就返回null
 }

/**
  * "按位與"來(lái)獲取數(shù)組下標(biāo)
  */
 static int indexFor(int h, int length) {
  return h & (length - 1);
 }

hashmap始終將自己的桶保持在2的n次方,這是為什么?indexFor這個(gè)方法解釋了這個(gè)問(wèn)題

大家都知道計(jì)算機(jī)里面位運(yùn)算是基本運(yùn)算,位運(yùn)算的效率是遠(yuǎn)遠(yuǎn)高于取余%運(yùn)算的

舉個(gè)例子:

2^n轉(zhuǎn)換成二進(jìn)制就是1+n個(gè)0,減1之后就是0+n個(gè)1,如16 -> 10000,15 -> 01111

那么根據(jù)&位運(yùn)算的規(guī)則,都為1(真)時(shí),才為1,那0≤運(yùn)算后的結(jié)果≤15,假設(shè)h <= 15,那么運(yùn)算后的結(jié)果就是h本身,h >15,運(yùn)算后的結(jié)果就是最后四位二進(jìn)制做&運(yùn)算后的值,最終,就是%運(yùn)算后的余數(shù)。

當(dāng)容量一定是2^n時(shí),h & (length - 1) == h % length

補(bǔ)充知識(shí):HashMap容量和負(fù)載因子

HashMap底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組+鏈表,JDK1.8中還引入了紅黑樹,當(dāng)鏈表長(zhǎng)度超過(guò)8個(gè)時(shí),會(huì)將鏈表轉(zhuǎn)成紅黑樹,以提升其查找性能。那么,給出一個(gè)<key, value>節(jié)點(diǎn),HashMap是如何確定這個(gè)節(jié)點(diǎn)應(yīng)該放在具體哪個(gè)位置呢?(以JDK1.8為例)

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
  Node<K,V>[] tab; Node<K,V> p; int n, i;
  // HashMap沒(méi)有被初始化,則先進(jìn)行初始化
  if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;
  // 節(jié)點(diǎn)所在index = (n - 1) & hash,該位置沒(méi)有數(shù)據(jù),則直接將新節(jié)點(diǎn)放在數(shù)組的index位置上
  if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);
  else { // index上已經(jīng)有節(jié)點(diǎn)了
    Node<K,V> e; K k;
    // 如果新key與原來(lái)的key一樣,則e指向原節(jié)點(diǎn)p(后面會(huì)用新value替換e所指向的value)
    if (p.hash == hash &&
      ((k = p.key) == key || (key != null && key.equals(k)))) 
      e = p;
    // 如果該節(jié)點(diǎn)是樹節(jié)點(diǎn),則采用樹的插入算法,插入新節(jié)點(diǎn)
    else if (p instanceof HashMap.TreeNode)
      e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    else { // 該節(jié)點(diǎn)是鏈表節(jié)點(diǎn)
      for (int binCount = 0; ; ++binCount) {
        // 將新節(jié)點(diǎn)插入到index所在鏈表的末端
        if ((e = p.next) == null) {
          p.next = newNode(hash, key, value, null);
          // 鏈表節(jié)點(diǎn)超過(guò)8個(gè),則進(jìn)行鏈表轉(zhuǎn)樹處理
          if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
            treeifyBin(tab, hash);
          break;
        }
        // 同樣的,如果key已經(jīng)存在的話,則不進(jìn)行插入操作,而是后面進(jìn)行value替換
        if (e.hash == hash &&
          ((k = e.key) == key || (key != null && key.equals(k))))
          break;
        p = e;
      }
    }
    // e != null的情況,就是key已經(jīng)存在了,這里統(tǒng)一進(jìn)行了新值value,替換舊值e.value的操作
    if (e != null) { // existing mapping for key
      V oldValue = e.value;
      if (!onlyIfAbsent || oldValue == null)
        e.value = value;
      afterNodeAccess(e);
      return oldValue;
    }
  }
  ++modCount;
  // 插入后數(shù)組size 大于閾值的話,需要進(jìn)行擴(kuò)容
  if (++size > threshold)
    resize();
  afterNodeInsertion(evict);
  return null;
}

看源碼,節(jié)點(diǎn)落在數(shù)組中的index = (數(shù)組長(zhǎng)度 - 1) & key的hashcode,如果該index上沒(méi)有數(shù)據(jù),則直接插到該index上,如果節(jié)點(diǎn)已經(jīng)有數(shù)據(jù)了,則把新節(jié)點(diǎn)插入該index對(duì)應(yīng)的鏈表中(如果鏈表節(jié)點(diǎn)大于8個(gè),會(huì)進(jìn)行鏈表轉(zhuǎn)樹,之后的插入算法就變成了樹的插入算法)。

每次put之后,會(huì)檢測(cè)一下是否需要擴(kuò)容,size超過(guò)了 總?cè)萘?* 負(fù)載因子,則會(huì)擴(kuò)容。默認(rèn)情況下,16 * 0.75 = 12個(gè)。

1、為什么初始容量是16

當(dāng)容量為2的冪時(shí),上述n -1 對(duì)應(yīng)的二進(jìn)制數(shù)全為1,這樣才能保證它和key的hashcode做&運(yùn)算后,能夠均勻分布,這樣才能減少hash碰撞的次數(shù)。至于默認(rèn)值為什么是16,而不是2 、4、8,或者32、64、1024等,我想應(yīng)該就是個(gè)折中處理,過(guò)小會(huì)導(dǎo)致放不下幾個(gè)元素,就要進(jìn)行擴(kuò)容了,而擴(kuò)容是一個(gè)很消耗性能的操作。取值過(guò)大的話,無(wú)疑會(huì)浪費(fèi)更多的內(nèi)存空間。因此在日常開(kāi)發(fā)中,如果可以預(yù)估HashMap會(huì)存入節(jié)點(diǎn)的數(shù)量,則應(yīng)該在初始化時(shí),指定其容量。

2、為什么負(fù)載因子是0.75

也是一個(gè)綜合考慮,如果設(shè)置過(guò)小,HashMap每put少量的數(shù)據(jù),都要進(jìn)行一次擴(kuò)容,而擴(kuò)容操作會(huì)消耗大量的性能。如果設(shè)置過(guò)大的話,如果設(shè)成1,容量還是16,假設(shè)現(xiàn)在數(shù)組上已經(jīng)占用的15個(gè),再要put數(shù)據(jù)進(jìn)來(lái),計(jì)算數(shù)組index時(shí),發(fā)生hash碰撞的概率將達(dá)到15/16,這違背的HashMap減少hash碰撞的原則。

以上這篇談?wù)凥ashmap的容量為什么是2的冪次問(wèn)題就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SWT(JFace)體驗(yàn)之Sash(活動(dòng)控件)

    SWT(JFace)體驗(yàn)之Sash(活動(dòng)控件)

    SWT(JFace)體驗(yàn)之Sash(活動(dòng)控件)
    2009-06-06
  • Jenkins中自動(dòng)化部署Spring?Boot項(xiàng)目的全過(guò)程

    Jenkins中自動(dòng)化部署Spring?Boot項(xiàng)目的全過(guò)程

    這篇文章主要介紹了如何使用Jenkins從Git倉(cāng)庫(kù)拉取SpringBoot項(xiàng)目并進(jìn)行自動(dòng)化部署,通過(guò)配置Jenkins任務(wù),實(shí)現(xiàn)項(xiàng)目的構(gòu)建、鏡像構(gòu)建和容器運(yùn)行,確保項(xiàng)目在更新時(shí)自動(dòng)部署,需要的朋友可以參考下
    2025-01-01
  • Spring Boot中快速操作Mongodb數(shù)據(jù)庫(kù)指南

    Spring Boot中快速操作Mongodb數(shù)據(jù)庫(kù)指南

    這篇文章主要給大家介紹了關(guān)于Spring Boot中如何快速操作Mongodb的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • IDEA生成javadoc的實(shí)現(xiàn)步驟

    IDEA生成javadoc的實(shí)現(xiàn)步驟

    Javadoc是一種用于生成API文檔的工具,它可以根據(jù)代碼中特定格式的注釋自動(dòng)生成文檔,本文主要介紹了IDEA生成javadoc的實(shí)現(xiàn)步驟,感興趣的可以了解一下
    2023-10-10
  • java逐行讀取文件(讀取文件每一行、按行讀取文件)附帶詳細(xì)代碼

    java逐行讀取文件(讀取文件每一行、按行讀取文件)附帶詳細(xì)代碼

    這篇文章主要給大家介紹了關(guān)于java逐行讀取文件(讀取文件每一行、按行讀取文件)的相關(guān)資料,讀取文件是我們?cè)谌粘9ぷ髦薪?jīng)常遇到的一個(gè)需求,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-09-09
  • 使用redisTemplate從redis獲取所有數(shù)據(jù)

    使用redisTemplate從redis獲取所有數(shù)據(jù)

    這篇文章主要介紹了使用redisTemplate從redis獲取所有數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Mybatis調(diào)用SQL?Server存儲(chǔ)過(guò)程的實(shí)現(xiàn)示例

    Mybatis調(diào)用SQL?Server存儲(chǔ)過(guò)程的實(shí)現(xiàn)示例

    在軟件開(kāi)發(fā)過(guò)程中,經(jīng)常會(huì)使用到存儲(chǔ)過(guò)程,本文就來(lái)介紹一下Mybatis調(diào)用SQL?Server存儲(chǔ)過(guò)程的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • IDEA自動(dòng)補(bǔ)全返回值的三種快捷方式

    IDEA自動(dòng)補(bǔ)全返回值的三種快捷方式

    平常在編碼的過(guò)程中,可能需要調(diào)用第三方Api接口,這個(gè)過(guò)程中可能涉及到不太熟悉第三方Api接口的返回值類型,平常在編碼的過(guò)程中,可能需要調(diào)用第三方Api接口,這個(gè)過(guò)程中可能涉及到不太熟悉第三方Api接口的返回值類型,需要的朋友可以參考下
    2023-10-10
  • 詳解java中的Collections類

    詳解java中的Collections類

    這篇文章主要為大家詳細(xì)介紹了java中的Collections類,感興趣的小伙伴們可以參考一下
    2016-07-07
  • 開(kāi)發(fā)工具EesyCode使用方法解析

    開(kāi)發(fā)工具EesyCode使用方法解析

    這篇文章主要介紹了開(kāi)發(fā)工具EesyCode使用方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04

最新評(píng)論

梁河县| 盘山县| 珠海市| 沁源县| 龙里县| 苏尼特右旗| 鸡东县| 三原县| 滨州市| 永嘉县| 许昌市| 平阳县| 镶黄旗| 手游| 徐水县| 乌拉特后旗| 靖江市| 滁州市| 长葛市| 正蓝旗| 贡山| 绥中县| 福海县| 桐乡市| 花莲县| 芦山县| 惠东县| 克什克腾旗| 卢龙县| 邹平县| 沙雅县| 郑州市| 明星| 灵山县| 秦皇岛市| 青海省| 信阳市| 偏关县| 顺昌县| 密云县| 常州市|