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

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

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

HashMap中的幾個(gè)重要變量

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

static final int DEFAULT_INITIAL_CAPACITY = 16;

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

static final int MAXIMUM_CAPACITY = 1 << 30;

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

static final float DEFAULT_LOAD_FACTOR = 0.75f;

用來(lái)存儲(chǔ)鍵值對(duì),可以看到鍵值對(duì)都是存儲(chǔ)在Entry中的

transient Entry<K,V>[] table;

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

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

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

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

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

Entry是一個(gè)鏈表結(jié)構(gòu),不僅包含key和value,還有可以指向下一個(gè)的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;
 }

首先通過(guò)hash方法對(duì)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值上做了一些處理,通過(guò)hash計(jì)算出來(lái)的值將會(huì)使用indexFor方法找到它應(yīng)該所在的table下標(biāo):

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

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

當(dāng)需要插入的key為null時(shí),調(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]這個(gè)位置開(kāi)始遍歷,因?yàn)閗ey為null只放在table中的第一個(gè)位置,下標(biāo)為0,在遍歷中如果發(fā)現(xiàn)已經(jīng)有key為null了,則替換新value,返回舊value,結(jié)束;如果還沒(méi)有key為null,調(diào)用addEntry方法增加一個(gè)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中的那個(gè)槽中已經(jīng)有Entry時(shí),才會(huì)發(fā)生resize。即有可能雖然size>=threshold,但是必須等到每個(gè)槽都至少有一個(gè)Entry時(shí),才會(huì)擴(kuò)容。還有注意每次resize都會(huì)擴(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,它先保存這個(gè)桶中的第一個(gè)Entry,創(chuàng)建新的Entry放入第一個(gè)位置,將原來(lái)的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時(shí),還是去table[0]去取:

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;
 }

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

hash的原則

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

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

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

解決hash碰撞的方法:

  • 開(kāi)放地址法
  • 鏈地址法

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

和jdk8中的HashMap區(qū)別

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

總結(jié)

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

相關(guān)文章

  • Java 判斷一個(gè)時(shí)間是否在另一個(gè)時(shí)間段內(nèi)

    Java 判斷一個(gè)時(shí)間是否在另一個(gè)時(shí)間段內(nèi)

    這篇文章主要介紹了Java 判斷一個(gè)時(shí)間是否在另一個(gè)時(shí)間段內(nèi)的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • SpringBoot整合Web之AOP配置詳解

    SpringBoot整合Web之AOP配置詳解

    面向切面編程(aspect-oriented programming,AOP)主要實(shí)現(xiàn)的目的是針對(duì)業(yè)務(wù)處理過(guò)程中的切面進(jìn)行提取,諸如日志、事務(wù)管理和安全這樣的系統(tǒng)服務(wù),從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開(kāi)發(fā)的效率
    2022-08-08
  • Springboot動(dòng)態(tài)切換數(shù)據(jù)源的具體實(shí)現(xiàn)與原理分析

    Springboot動(dòng)態(tài)切換數(shù)據(jù)源的具體實(shí)現(xiàn)與原理分析

    目前有個(gè)需求,需要使用不同的數(shù)據(jù)源,例如某業(yè)務(wù)要用A數(shù)據(jù)源,另一個(gè)業(yè)務(wù)要用B數(shù)據(jù)源,所以下面這篇文章主要給大家介紹了關(guān)于Springboot動(dòng)態(tài)切換數(shù)據(jù)源的具體實(shí)現(xiàn)與原理分析,需要的朋友可以參考下
    2021-12-12
  • 學(xué)習(xí)Java設(shè)計(jì)模式之觀察者模式

    學(xué)習(xí)Java設(shè)計(jì)模式之觀察者模式

    這篇文章主要為大家介紹了Java設(shè)計(jì)模式中的觀察者模式,對(duì)Java設(shè)計(jì)模式感興趣的小伙伴們可以參考一下
    2016-01-01
  • lombok注解@Data使用在繼承類上時(shí)出現(xiàn)警告的問(wèn)題及解決

    lombok注解@Data使用在繼承類上時(shí)出現(xiàn)警告的問(wèn)題及解決

    Lombok的@Data注解簡(jiǎn)化了實(shí)體類代碼,但在子類中使用時(shí)會(huì)出現(xiàn)警告,指出equals和hashCode方法不會(huì)考慮父類屬性,解決方法有兩種:一是在父類上使用@EqualsAndHashCode(callSuper=true)注解;二是通過(guò)配置lombok.config文件,均能有效解決警告問(wèn)題
    2024-10-10
  • Java 深入淺出講解代理模式

    Java 深入淺出講解代理模式

    代理模式是Java常見(jiàn)的設(shè)計(jì)模式之一。所謂代理模式是指客戶端并不直接調(diào)用實(shí)際的對(duì)象,而是通過(guò)調(diào)用代理,來(lái)間接的調(diào)用實(shí)際的對(duì)象
    2022-03-03
  • JavaWeb Refresh響應(yīng)頭代碼實(shí)例詳解

    JavaWeb Refresh響應(yīng)頭代碼實(shí)例詳解

    這篇文章主要介紹了JavaWeb Refresh響應(yīng)頭代碼實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Java中的三種標(biāo)準(zhǔn)注解和四種元注解說(shuō)明

    Java中的三種標(biāo)準(zhǔn)注解和四種元注解說(shuō)明

    這篇文章主要介紹了Java中的三種標(biāo)準(zhǔn)注解和四種元注解說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • JavaFX實(shí)現(xiàn)UI美觀效果代碼實(shí)例

    JavaFX實(shí)現(xiàn)UI美觀效果代碼實(shí)例

    這篇文章主要介紹了JavaFX實(shí)現(xiàn)UI美觀效果代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Velocity基本語(yǔ)法介紹

    Velocity基本語(yǔ)法介紹

    以下是對(duì)Velocity的基本語(yǔ)法進(jìn)行了深入的介紹。需要的朋友可以過(guò)來(lái)參考下
    2013-08-08

最新評(píng)論