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

jdk7 中HashMap的知識點(diǎn)總結(jié)

 更新時間:2017年01月21日 16:44:22   投稿:daisy  
HashMap的原理是老生常談了,不作仔細(xì)解說。一句話概括為HashMap是一個散列表,它存儲的內(nèi)容是鍵值對(key-value)映射。這篇文章主要總結(jié)了關(guān)于jdk7 中HashMap的知識點(diǎn),需要的朋友可以參考借鑒,一起來看看吧。

HashMap中的幾個重要變量

默認(rèn)初始容量,必須是2的n次方

static final int DEFAULT_INITIAL_CAPACITY = 16;

最大容量,當(dāng)通過構(gòu)造方法傳入的容量比它還大時,就用這個最大容量,必須是2的n次方

static final int MAXIMUM_CAPACITY = 1 << 30;

默認(rèn)負(fù)載因子

static final float DEFAULT_LOAD_FACTOR = 0.75f;

用來存儲鍵值對,可以看到鍵值對都是存儲在Entry中的

transient Entry<K,V>[] table;

//capacity * load factor,超過這個數(shù)就會進(jìn)行再哈希
int threshold;

HashMap中的元素是用名為table的Entry數(shù)組來保存的,默認(rèn)大小是16

  • capacity:數(shù)組的容量
  • load_factor:負(fù)載因子
  • threshold:實(shí)際能承載的容量,等于上面兩個相乘,當(dāng)size大于threshold時,就會進(jìn)行rehash

jdk7中在面對key為String的時候采用了區(qū)別對待,會有alternative hashing,但是這個在jdk8中已經(jīng)被刪除了

存儲結(jié)構(gòu)

Entry是一個鏈表結(jié)構(gòu),不僅包含key和value,還有可以指向下一個的next

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

 /**
  * Creates new entry.
  */
 Entry(int h, K k, V v, Entry<K,V> n) {
  value = v;
  next = n;
  key = k;
  hash = h;
 }
 ...

put方法

public V put(K key, V value) {
 if (key == null)
  return putForNullKey(value);
 int hash = hash(key);
 int i = indexFor(hash, table.length);
 for (Entry<K,V> e = table[i]; e != null; e = e.next) {
  Object k;
  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);
 return null;
 }

首先通過hash方法對hashcode進(jìn)行處理:

final int hash(Object k) {
 int h = 0;
 h ^= k.hashCode();

 h ^= (h >>> 20) ^ (h >>> 12);
 return h ^ (h >>> 7) ^ (h >>> 4);
 }

可以看到只是在key的hashcode值上做了一些處理,通過hash計算出來的值將會使用indexFor方法找到它應(yīng)該所在的table下標(biāo):

static int indexFor(int h, int length) {
 return h & (length-1);
 }

這個方法其實(shí)相當(dāng)于對table.length取模。

當(dāng)需要插入的key為null時,調(diào)用putForNullKey方法處理:

 private V putForNullKey(V value) {
 for (Entry<K,V> e = table[0]; e != null; e = e.next) {
  if (e.key == null) {
  V oldValue = e.value;
  e.value = value;
  e.recordAccess(this);
  return oldValue;
  }
 }
 modCount++;
 addEntry(0, null, value, 0);
 return null;
 }

putForNullKey方法只從table[0]這個位置開始遍歷,因?yàn)閗ey為null只放在table中的第一個位置,下標(biāo)為0,在遍歷中如果發(fā)現(xiàn)已經(jīng)有key為null了,則替換新value,返回舊value,結(jié)束;如果還沒有key為null,調(diào)用addEntry方法增加一個Entry:

void addEntry(int hash, K key, V value, int bucketIndex) {
 if ((size >= threshold) && (null != table[bucketIndex])) {
  resize(2 * table.length);
  hash = (null != key) ? hash(key) : 0;
  bucketIndex = indexFor(hash, table.length);
 }

 createEntry(hash, key, value, bucketIndex);
 }

可以看到j(luò)dk7中resize的條件已經(jīng)發(fā)生改變了,只有當(dāng) size>=threshold并且 table中的那個槽中已經(jīng)有Entry時,才會發(fā)生resize。即有可能雖然size>=threshold,但是必須等到每個槽都至少有一個Entry時,才會擴(kuò)容。還有注意每次resize都會擴(kuò)大一倍容量

void createEntry(int hash, K key, V value, int bucketIndex) {
 Entry<K,V> e = table[bucketIndex];
 table[bucketIndex] = new Entry<>(hash, key, value, e);
 size++;
 }

最后看createEntry,它先保存這個桶中的第一個Entry,創(chuàng)建新的Entry放入第一個位置,將原來的Entry接在后面。這里采用的是頭插法插入元素。

get方法

其實(shí)get方法和put方法如出一轍,怎么放的怎么拿

public V get(Object key) {
 if (key == null)
  return getForNullKey();
 Entry<K,V> entry = getEntry(key);

 return null == entry ? null : entry.getValue();
 }

key為null時,還是去table[0]去?。?/p>

private V getForNullKey() {
 for (Entry<K,V> e = table[0]; e != null; e = e.next) {
  if (e.key == null)
  return e.value;
 }
 return null;
 }

否則調(diào)用getEntry方法:

final Entry<K,V> getEntry(Object key) {
 int hash = (key == null) ? 0 : hash(key);
 for (Entry<K,V> e = table[indexFor(hash, table.length)];
  e != null;
  e = e.next) {
  Object k;
  if (e.hash == hash &&
  ((k = e.key) == key || (key != null && key.equals(k))))
  return e;
 }
 return null;
 }

這個方法也是通過key的hashcode計算出它應(yīng)該所在的下標(biāo),再遍歷這個下標(biāo)的Entry鏈,如果key的內(nèi)存地址相等(即同一個引用)或者equals相等,則說明找到了

hash的原則

A、等冪性。不管執(zhí)行多少次獲取Hash值的操作,只要對象不變,那么Hash值是固定的。如果第一次取跟第N次取不一樣,那就用起來很麻煩.

B、對等性。若兩個對象equal方法返回為true,則其hash值也應(yīng)該是一樣的。舉例說明:若你將objA作為key存入HashMap中,然后new了一個objB。在你看來objB和objA是一個東西(因?yàn)樗麄僥qual),但是使用objB到hashMap中卻取不出來東西。

C、互異性。若兩個對象equal方法返回為false,hash值有可能相同,但最好是不同的,這個不是必須的,只是這樣做會提高h(yuǎn)ash類操作的性能(碰撞幾率低)。

解決hash碰撞的方法:

  • 開放地址法
  • 鏈地址法

hashmap采用的就是鏈地址法,這種方法好處是無堆積現(xiàn)象,但是next指針會占用額外空間

和jdk8中的HashMap區(qū)別

在jdk8中,仍然會根據(jù)key.hashCode()計算出hash值,再通過這個hash值去定位這個key,但是不同的是,當(dāng)發(fā)生沖突時,會采用鏈表和紅黑樹兩種方法去處理,當(dāng)結(jié)點(diǎn)個數(shù)較少時用鏈表(用Node存儲),個數(shù)較多時用紅黑樹(用TreeNode存儲),同時結(jié)點(diǎn)也不叫Entry了,而是分成了Node和TreeNode。再最壞的情況下,鏈表查找的時間復(fù)雜度為O(n),而紅黑樹一直是O(logn),這樣會提高HashMap的效率。jdk8中的HashMap中定義了一個變量TREEIFY_THRESHOLD,當(dāng)節(jié)點(diǎn)個數(shù)>= TREEIFY_THRESHOLD - 1時,HashMap將采用紅黑樹存儲

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。

相關(guān)文章

  • Java 集合框架之List 的使用(附小游戲練習(xí))

    Java 集合框架之List 的使用(附小游戲練習(xí))

    這篇文章主要介紹Java 集合框架中List 的使用,下面文章將圍繞Java 集合框架中List 的使用展開話題,并附上一些小游戲練習(xí),需要的朋友可以參考一下
    2021-10-10
  • 基于SpringAOP+Caffeine實(shí)現(xiàn)本地緩存的實(shí)例代碼

    基于SpringAOP+Caffeine實(shí)現(xiàn)本地緩存的實(shí)例代碼

    公司想對一些不經(jīng)常變動的數(shù)據(jù)做一些本地緩存,我們使用AOP+Caffeine來實(shí)現(xiàn),所以本文給大家介紹了
    基于SpringAOP+Caffeine實(shí)現(xiàn)本地緩存的實(shí)例,文中有詳細(xì)的代碼供大家參考,需要的朋友可以參考下
    2024-03-03
  • Spring Boot與Spark、Cassandra系統(tǒng)集成開發(fā)示例

    Spring Boot與Spark、Cassandra系統(tǒng)集成開發(fā)示例

    本文演示以Spark作為分析引擎,Cassandra作為數(shù)據(jù)存儲,而使用Spring Boot來開發(fā)驅(qū)動程序的示例。對spring boot 與spark cassandra集成開發(fā)示例代碼感興趣的朋友跟著腳本之家小編一起學(xué)習(xí)吧
    2018-02-02
  • springboot訪問404問題的解決辦法

    springboot訪問404問題的解決辦法

    工作中遇到url404問題,解決問題的進(jìn)程比較崎嶇,寫篇文章記錄,下面這篇文章主要給大家介紹了關(guān)于springboot訪問404問題的解決辦法,文中通過圖文介紹的非常詳細(xì),要的朋友可以參考下
    2023-03-03
  • SpringBoot Redis配置Fastjson進(jìn)行序列化和反序列化實(shí)現(xiàn)

    SpringBoot Redis配置Fastjson進(jìn)行序列化和反序列化實(shí)現(xiàn)

    這篇文章主要介紹了SpringBoot Redis配置Fastjson進(jìn)行序列化和反序列化實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • MyBatis入門學(xué)習(xí)教程(一)-MyBatis快速入門

    MyBatis入門學(xué)習(xí)教程(一)-MyBatis快速入門

    MyBatis是一個支持普通SQL查詢,存儲過程和高級映射的優(yōu)秀持久層框架,這篇文章主要給大家分享MyBatis入門學(xué)習(xí)教程(一)-MyBatis快速入門,需要的朋友可以參考下
    2015-08-08
  • Spring Security實(shí)現(xiàn)退出登錄和退出處理器

    Spring Security實(shí)現(xiàn)退出登錄和退出處理器

    本文主要介紹了Spring Security實(shí)現(xiàn)退出登錄和退出處理器,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • java獲取日期的方法

    java獲取日期的方法

    這篇文章介紹了java獲取日期的方法,有需要的朋友可以參考一下
    2013-10-10
  • Java中重寫和重載的區(qū)別及說明

    Java中重寫和重載的區(qū)別及說明

    Java語言中的重載和重寫是實(shí)現(xiàn)多態(tài)的兩種方式,但他們的實(shí)現(xiàn)方式和規(guī)則有所不同,重載發(fā)生在一個類中,同名的方法如果有不同的參數(shù)列表,則視為重載,重寫則發(fā)生在子類和父類之間,要求子類重寫方法和父類被重寫方法有相同的返回類型
    2024-10-10
  • 你知道怎么從Python角度學(xué)習(xí)Java基礎(chǔ)

    你知道怎么從Python角度學(xué)習(xí)Java基礎(chǔ)

    這篇文章主要為大家詳細(xì)介紹了Python角度學(xué)習(xí)Java基礎(chǔ)的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02

最新評論

陈巴尔虎旗| 望都县| 平原县| 衢州市| 杭锦旗| 石楼县| 无极县| 冕宁县| 娄底市| 荣昌县| 双峰县| 嵊州市| 桦川县| 旬阳县| 濉溪县| 白水县| 西乌珠穆沁旗| 楚雄市| 成安县| 广州市| 佳木斯市| 韶关市| 扎囊县| 玛纳斯县| 类乌齐县| 桦南县| 旌德县| 防城港市| 石河子市| 江华| 鹤庆县| 台南市| 信丰县| 简阳市| 四会市| 瑞丽市| 敦煌市| 孟津县| 隆子县| 南靖县| 丹巴县|