溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JDK的HashMap怎么使用

發(fā)布時(shí)間:2021-12-31 16:02:53 來源:億速云 閱讀:126 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“JDK的HashMap怎么使用”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“JDK的HashMap怎么使用”吧!

首先先來分析一下HashMap

static final int DEFAULT_INITIAL_CAPACITY = 16;

最大容量2的15次方+1;
static final int MAXIMUM_CAPACITY = 1 << 30;

默認(rèn)加載因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;

內(nèi)部實(shí)現(xiàn)的機(jī)制是用具有鍵值對(duì)格式的單個(gè)的entry數(shù)組實(shí)現(xiàn):

  1. transient Entry[] table;


  2.    static class Entry<K,V> implements Map.Entry<K,V> {

  3.         final K key; //鍵

  4.         V value;    // 值

  5.         Entry<K,V> next;  //下一個(gè)

  6.         final int hash;   //哈西值


  7.         /**

  8.          * Creates new entry.

  9.          */

  10.         Entry(int h, K k, V v, Entry<K,V> n) {

  11.             value = v;

  12.             next = n;

  13.             key = k;

  14.             hash = h;

  15.         }


  16.         public final K getKey() {

  17.             return key;

  18.         }


  19.         public final V getValue() {

  20.             return value;

  21.         }


  22.         public final V setValue(V newValue) {

  23.         V oldValue = value;

  24.             value = newValue;

  25.             return oldValue;

  26.         }


  27.         public final boolean equals(Object o) {

  28.             if (!(o instanceof Map.Entry))

  29.                 return false;

  30.             Map.Entry e = (Map.Entry)o;

  31.             Object k1 = getKey();

  32.             Object k2 = e.getKey();

  33.             if (k1 == k2 || (k1 != null && k1.equals(k2))) {

  34.                 Object v1 = getValue();

  35.                 Object v2 = e.getValue();

  36.                 if (v1 == v2 || (v1 != null && v1.equals(v2)))

  37.                     return true;

  38.             }

  39.             return false;

  40.         }


  41.         public final int hashCode() {

  42.             return (key==null   ? 0 : key.hashCode()) ^

  43.                    (value==null ? 0 : value.hashCode());

  44.         }


  45.         public final String toString() {

  46.             return getKey() + "=" + getValue();

  47.         }



  48.         void recordAccess(HashMap<K,V> m) {

  49.         }


  50.         void recordRemoval(HashMap<K,V> m) {

  51.         }

  52.     }

當(dāng)前的數(shù)組大小:
transient int size;

構(gòu)造函數(shù)初始化:
設(shè)置初始化的容積和加載因子

  1. public HashMap(int initialCapacity, float loadFactor) {


  2.         //初始化容積必須大于0

  3.         if (initialCapacity < 0)

  4.             throw new IllegalArgumentException("Illegal initial capacity: " +

  5.                                                initialCapacity);

  6.         //超過最大容積的時(shí)候,那么等于最大容積

  7.         if (initialCapacity > MAXIMUM_CAPACITY)

  8.             initialCapacity = MAXIMUM_CAPACITY;


  9.         //初始化加載因子;

  10.         if (loadFactor <= 0 || Float.isNaN(loadFactor))

  11.             throw new IllegalArgumentException("Illegal load factor: " +

  12.                                                loadFactor);


  13.         //找出一個(gè)數(shù)的平方大于當(dāng)前給出的初始化容積,比如是初始化是10的話,那么最終的容積就是16

  14.         int capacity = 1;

  15.         while (capacity < initialCapacity)

  16.             capacity <<= 1;


  17.         this.loadFactor = loadFactor;

  18.         threshold = (int)(capacity * loadFactor);


  19.         //用容積來初始化數(shù)組大??;size當(dāng)前還是為0;

  20.         table = new Entry[capacity];


  21.         //初始化動(dòng)作,可以留給子類實(shí)現(xiàn),模板模式的應(yīng)用

  22.         init();

  23.     }


 這是一個(gè)hashcode的轉(zhuǎn)換算法;他能夠把對(duì)象的hashcode轉(zhuǎn)換成為小于length-1的整數(shù)作為數(shù)組的下標(biāo);那么勢(shì)必會(huì)出現(xiàn)重合

  1.     static int hash(int h) {

  2.         h ^= (h >>> 20) ^ (h >>> 12);

  3.         return h ^ (h >>> 7) ^ (h >>> 4);

  4.     }


  5.     static int indexFor(int h, int length) {

  6.         return h & (length-1);

  7.     }



我們先看增加方法:

  1.  public V put(K key, V value) {

  2.         //判斷鍵值是否為空;

  3.         if (key == null)

  4.             return putForNullKey(value);


  5.         //得到hashcode

  6.         int hash = hash(key.hashCode());

  7.         //得到一個(gè)小于數(shù)組長度(取的是與操作)的下標(biāo)

  8.         int i = indexFor(hash, table.length);

  9.         //在數(shù)組中找到該下標(biāo)的entry值;

  10.         //事實(shí)上entry也是一個(gè)鏈表,相對(duì)于LinkedList來說,他的entry是單向的

  11.         for (Entry<K,V> e = table[i]; e != null; e = e.next) {

  12.             Object k;

  13.             //如果存在鍵值是同一對(duì)象的entry,那么用新值覆蓋舊值,不存在則再往下找,知道末尾

  14.             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

  15.                 V oldValue = e.value;

  16.                 e.value = value;

  17.                 e.recordAccess(this);

  18.                 return oldValue;

  19.             }

  20.         }

  21.         //增加修改次數(shù)

  22.         modCount++;

  23.         //增加這個(gè)entry到該下標(biāo)列表的首部

  24.         addEntry(hash, key, value, i);

  25.         return null;

  26.     }


  27.     void addEntry(int hash, K key, V value, int bucketIndex) {

  28.     Entry<K,V> e = table[bucketIndex];

  29.         //可見是放在該下標(biāo)鏈表的第一位的

  30.         table[bucketIndex] = new Entry<K,V>(hash, key, value, e);

  31.         //在這里增加了size;

  32.         if (size++ >= threshold)

  33.             resize(2 * table.length);

  34.     }

如果key是null的話:
 那么他可以不用通過hashcode定位數(shù)組中的隊(duì)列下標(biāo)-_-||事實(shí)上他也沒有hashcode;規(guī)定在0位存放這個(gè)鏈表的頭;可見在HashMap中是可以

存放null的key的;但是正因?yàn)槠錄]有hashcode那么就只能存放一個(gè)元素,而不是像其他一樣能存放多個(gè);但是另外我們可見,你可以考慮把使

用的最多的值的鍵設(shè)置成為null,因?yàn)樗钦业降淖羁斓模?/p>


  1. private V putForNullKey(V value) {

  2.         for (Entry<K,V> e = table[0]; e != null; e = e.next) {

  3.             if (e.key == null) {

  4.                 V oldValue = e.value;

  5.                 e.value = value;

  6.                 e.recordAccess(this);

  7.                 return oldValue;

  8.             }

  9.         }

  10.         modCount++;

  11.         addEntry(0, null, value, 0);

  12.         return null;

  13.     }


上面說了存放了,下面我們?cè)賮砜纯慈绾稳〕鰜戆眩?/p>

  1. final Entry<K,V> getEntry(Object key) {

  2.         //經(jīng)過算法得到這個(gè)key對(duì)應(yīng)的hashcode,可見hashcode是固定的對(duì)應(yīng),而不是隨機(jī)的;如果是null的話為0,經(jīng)過與操作還是0,直接 


  3.        //定位到table[0]否則查找出下標(biāo)

  4.        int hash = (key == null) ? 0 : hash(key.hashCode());

  5.         for (Entry<K,V> e = table[indexFor(hash, table.length)];

  6.              e != null;

  7.              e = e.next) {

  8.             Object k;

  9.      //匹配這個(gè)key的hashcode和數(shù)值,可見不是同一的值其hashcode是可能一樣的,否則如果是一一對(duì)應(yīng)則沒必要匹配key了

  10.             if (e.hash == hash &&

  11.                 ((k = e.key) == key || (key != null && key.equals(k))))

  12.                 return e;

  13.         } 

  14.     //沒有找到,為空;

  15.        return null;

我們?cè)賮砜纯雌淙萘渴侨绾卧鲩L的:

  1.     public void putAll(Map<? extends K, ? extends V> m) {

  2.         //得到新增加進(jìn)來的元素的個(gè)數(shù)

  3.         int numKeysToBeAdded = m.size();

  4.         if (numKeysToBeAdded == 0)

  5.             return;

  6.          //如果新增加的元素個(gè)數(shù)大于原來容積*加載因子;可見我們必須要擴(kuò)充容量了

  7.         if (numKeysToBeAdded > threshold) {

  8.             //那么我們?cè)黾拥娜萘繉⑹窃撔略鲈貍€(gè)數(shù)+預(yù)留空間的總數(shù)

  9.             int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);

  10.             if (targetCapacity > MAXIMUM_CAPACITY)

  11.                 targetCapacity = MAXIMUM_CAPACITY;

  12.             int newCapacity = table.length;

  13.             //如果我們現(xiàn)在的表的容量小于新增加的容量,那么我們擴(kuò)展兩倍,如果還小,再擴(kuò)大兩倍,直到他的大于當(dāng)前需要的容量

  14.             while (newCapacity < targetCapacity)

  15.                 newCapacity <<= 1;

  16.             if (newCapacity > table.length)

  17.                 resize(newCapacity);

  18.         }


  19.         //添置新的"家具"

  20.         for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {

  21.             Map.Entry<? extends K, ? extends V> e = i.next();

  22.             put(e.getKey(), e.getValue());

  23.         }

  24.     }

用新的容積開辟了一塊足夠大的存儲(chǔ)空間,

  1. void resize(int newCapacity) {

  2.         Entry[] oldTable = table;

  3.         int oldCapacity = oldTable.length;

  4.         if (oldCapacity == MAXIMUM_CAPACITY) {

  5.             threshold = Integer.MAX_VALUE;

  6.             return;

  7.         }


  8.         Entry[] newTable = new Entry[newCapacity];

  9.         transfer(newTable);

  10.         table = newTable;

  11.         threshold = (int)(newCapacity * loadFactor);

  12.     }

  13. 并且把家具從以前的“小房子”幫到了“大房子”

  14.  void transfer(Entry[] newTable) {

  15.         Entry[] src = table;

  16.         int newCapacity = newTable.length;

  17.         for (int j = 0; j < src.length; j++) {

  18.             //得到各個(gè)數(shù)組元素的鏈表的頭

  19.             Entry<K,V> e = src[j];

  20.             if (e != null) {

  21.                 src[j] = null//一定要把原來的“小房子”收拾好,方便垃圾回收;

  22.                 do {

  23.                     Entry<K,V> next = e.next;

  24.                     int i = indexFor(e.hash, newCapacity);

  25.                     e.next = newTable[i];

  26.                     newTable[i] = e;

  27.                     e = next;

  28.                 } while (e != null);

  29.             }

  30.         }

  31.     }

新增,查找,都看過了,我們來看一下刪除:

  1. public V remove(Object key) {

  2.         Entry<K,V> e = removeEntryForKey(key);

  3.         return (e == null ? null : e.value);

  4.     }



  5.     final Entry<K,V> removeEntryForKey(Object key) {

  6.         //定位查找到鏈表表頭;

  7.         int hash = (key == null) ? 0 : hash(key.hashCode());

  8.         int i = indexFor(hash, table.length);

  9.         Entry<K,V> prev = table[i];

  10.         Entry<K,V> e = prev;


  11.         while (e != null) {

  12.             Entry<K,V> next = e.next;

  13.             Object k;

  14.             if (e.hash == hash &&

  15.                 ((k = e.key) == key || (key != null && key.equals(k)))) {

  16.                 modCount++;

  17.                 //若能找到,修改前后的鏈表節(jié)點(diǎn)的指向,從中刪除;

  18.                 size--;

  19.                 if (prev == e)

  20.                     table[i] = next;

  21.                 else

  22.                     prev.next = next;

  23.                 e.recordRemoval(this);

  24.                 return e;

  25.             }

  26.             //方便鏈表往下傳遞;保存prev是因?yàn)槭菃蜗蜴湵恚砸坏┱业降哪繕?biāo)節(jié)點(diǎn),無法通過該節(jié)點(diǎn)得到錢一個(gè)節(jié)點(diǎn),就無法更改前


  27. 一個(gè)節(jié)點(diǎn)的指//向,所以要保存prev

  28.             prev = e;

  29.             e = next;

  30.         }


  31.         return e;

  32.     }


JDK的HashMap怎么使用

現(xiàn)在我們?cè)倏匆幌氯绾螁为?dú)取到他的鍵值集合或者值集合:

  1. values實(shí)現(xiàn)了一個(gè)內(nèi)部私有類;

  2.   public Collection<V> values() {

  3.         Collection<V> vs = values;

  4.         return (vs != null ? vs : (values = new Values()));

  5.     }


  6. //AbstractCollection<V> 把一些基本的curd委托給了iteartor,所以只需重載其獲取iteartor的方法;

  7.     private final class Values extends AbstractCollection<V> {


  8.         //重載了iteartor

  9.         public Iterator<V> iterator() {

  10.             return newValueIterator();

  11.         }

  12.         public int size() {

  13.             return size;

  14.         }

  15.         public boolean contains(Object o) {

  16.             return containsValue(o);

  17.         }

  18.         public void clear() {

  19.             HashMap.this.clear();

  20.         }

  21.     }

  22. 下面是該iteartor的獲?。浩渲兄饕氖强纯次覀儷@取的value的collection是如何排序的;


  23.     private abstract class HashIterator<E> implements Iterator<E> {

  24.         Entry<K,V> next;    // next entry to return

  25.         int expectedModCount;   // For fast-fail

  26.         int index;      // current slot

  27.         Entry<K,V> current; // current entry


  28.         HashIterator() {

  29.             expectedModCount = modCount;

  30.             //如果該HashMap有元素

  31.             if (size > 0) { // advance to first entry

  32.                 Entry[] t = table;

  33.                 然后把next和index都調(diào)至該table數(shù)組的鏈表頭中不為空的地方;

  34.                 while (index < t.length && (next = t[index++]) == null)

  35.                     ;

  36.             }

  37.         }


  38.         public final boolean hasNext() {

  39.             return next != null;

  40.         }


  41.         final Entry<K,V> nextEntry() {

  42.             if (modCount != expectedModCount)

  43.                 throw new ConcurrentModificationException();

  44.             Entry<K,V> e = next;

  45.             if (e == null)

  46.                 throw new NoSuchElementException();

  47.             //代表這個(gè)鏈表遍歷完成了,需要開始遍歷下一個(gè)table下標(biāo)的鏈表了

  48.             if ((next = e.next) == null) {

  49.                 Entry[] t = table;

  50.             //找到下一個(gè)不為空的table元素

  51.                 while (index < t.length && (next = t[index++]) == null)

  52.                     ;

  53.             }

  54.         current = e;

  55.             return e;

  56.         }


  57.         public void remove() {

  58.             if (current == null)

  59.                 throw new IllegalStateException();

  60.             if (modCount != expectedModCount)

  61.                 throw new ConcurrentModificationException();

  62.             Object k = current.key;

  63.             current = null;

  64.             HashMap.this.removeEntryForKey(k);

  65.             expectedModCount = modCount;

  66.         }


  67.     }



  68. ValueIterator :


  69.  private final class ValueIterator extends HashIterator<V> {

  70.         public V next() {

  71.             return nextEntry().value;

  72.         }

  73.     }

這是我們會(huì)問加入我在這個(gè)value的collection中增加元素會(huì)出現(xiàn)什么樣的情況呢,次序會(huì)不會(huì)打亂,對(duì)不起,AbstractCollection不存在add

的,iteartor也是不允許增加元素的;

對(duì)于來說keySet來說是同理的,不過因?yàn)閗ey是不存在一樣的,所以,我們不會(huì)返回collection而返回set,避免了重復(fù)key的出現(xiàn)

到此,相信大家對(duì)“JDK的HashMap怎么使用”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI