日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HashTable 源码解读

發(fā)布時(shí)間:2023/11/29 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HashTable 源码解读 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

很多人都知道HashTable與HashMap的關(guān)系,HashTable是線程安全的,HashMap是非線程安全的。在介紹完HashMap之后,趁熱介紹一下HashTable。在HashTable中沒有像HashMap中那么多關(guān)于數(shù)據(jù)結(jié)構(gòu)的內(nèi)容。HashTable是線程安全的,因?yàn)槠湓创a的方法里都帶有synchronized,但是效率不高,如果想使用高性能的Hash結(jié)構(gòu),建議使用java.util.concurrent.ConcurrentHashMap

HashTable 存儲的數(shù)據(jù)類型

HashTable的key和value都可以為空,在存儲的過程中 key必須實(shí)現(xiàn) hashCode()和equals()兩個(gè)方法。

影響HashTable性能的兩個(gè)參數(shù)

HashTable中的兩個(gè)變量影響其性能:初始容量與負(fù)載系數(shù)(load factor)。

容量

指的時(shí)hashtable中桶的個(gè)數(shù)。桶其實(shí)就是單向的鏈表。hashtable 是允許hash 沖突的,單個(gè)桶(鏈表)可以存儲多個(gè)entry。在定義HashTable的初始容量的大小時(shí),要權(quán)衡是空間 和 重新hash運(yùn)算(很耗時(shí))之間的利弊。當(dāng)初始的容量大于元素的最大個(gè)數(shù)時(shí),將不會發(fā)生rehash運(yùn)算,但是太大的初始容量意味著浪費(fèi)了很多空間。如果能提前估算出要向hashTable中存很多值時(shí),就要給一個(gè)適合的初始容量,因?yàn)樵谔砑訑?shù)據(jù)時(shí)如,果不需要rehash操作的話將會更快。

負(fù)載系數(shù)

指的是hashtable在自動擴(kuò)容之前允許桶多滿?默認(rèn)的負(fù)載系數(shù)為0.75,增大可以減少每次擴(kuò)容的大小,但是增加了查找所花費(fèi)的時(shí)間。

數(shù)據(jù)結(jié)構(gòu)

前面也提到了,HashTable內(nèi)部存儲了一個(gè)table數(shù)組,這個(gè)數(shù)組的每一個(gè)元素存儲的都是鏈表的頭。在存儲數(shù)值時(shí),定位存儲位置是通過如下代碼:

int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length; // 去掉符號位的影響Entry<K,V> e = (Entry<K,V>)tab[index];

上面的代碼就確定了當(dāng)前key的節(jié)點(diǎn)位于哪個(gè)鏈表上,e 即鏈表頭。如果在該鏈表中無法找到對應(yīng)的key,則將當(dāng)前的節(jié)點(diǎn)添加到鏈表的頭部。

Entry<K,V> e = (Entry<K,V>) tab[index];// 把鏈表的頭部傳進(jìn)去,為了將new 出來的節(jié)點(diǎn).next指向原來鏈表的頭部tab[index] = new Entry<>(hash, key, value, e);

rehash算法

rehash算法,也可以理解為擴(kuò)容算法,當(dāng)table裝不下要存儲的值的時(shí)候,這是后就需要擴(kuò)容增加內(nèi)部數(shù)組的長度,這下慘了,每個(gè)key存儲到哪個(gè)鏈表中是和table.length有直接關(guān)系的,所以在擴(kuò)容時(shí),要把當(dāng)前hashtable中存儲的節(jié)點(diǎn)重新計(jì)算一遍存儲位置,這就是前面提到的為什么rehash會很耗時(shí)。

protected void rehash() {int oldCapacity = table.length;Entry<?,?>[] oldMap = table;// overflow-conscious codeint newCapacity = (oldCapacity << 1) + 1; // 容量每次擴(kuò)大一倍if (newCapacity - MAX_ARRAY_SIZE > 0) {if (oldCapacity == MAX_ARRAY_SIZE)// Keep running with MAX_ARRAY_SIZE bucketsreturn;newCapacity = MAX_ARRAY_SIZE;}Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];modCount++; //結(jié)構(gòu)變化threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);table = newMap;for (int i = oldCapacity ; i-- > 0 ;) {for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {Entry<K,V> e = old; // 將當(dāng)前的變量賦值給暫存變量old = old.next; // 繼續(xù)獲取鏈表的下一個(gè)節(jié)點(diǎn),為下一次循環(huán)做準(zhǔn)備int index = (e.hash & 0x7FFFFFFF) % newCapacity; // 計(jì)算當(dāng)前節(jié)點(diǎn)在newMap中存儲的位置// 每次插入數(shù)據(jù)都插入到鏈表的頭e.next = (Entry<K,V>)newMap[index]; // 將當(dāng)前節(jié)點(diǎn)的指針指向原來鏈表的頭newMap[index] = e; // 將當(dāng)且節(jié)點(diǎn)存入數(shù)組中}} }

compute方法

這不是hashMap獨(dú)有的,是Map接口定義的。放在這里講的原因是:HashTable沒什么好寫的,正好從HashMap把這部分內(nèi)容搬過來。

computeIfAbsent,computeIfPresent,compute 三個(gè)方法,這三個(gè)方法本質(zhì)上都是根據(jù)給定的key更新當(dāng)前map中的值,HashMap中也有同樣的方法
下面是一個(gè)簡短的例子

public static void main(String[] args) { HashMap<String,Integer> map = new HashMap<>(); for (int i = 0; i< 10; i++) { map.put(String.valueOf(i),i); } map.computeIfPresent(String.valueOf(5),new MyFunction()); // 如果存在則計(jì)算 System.out.println(map);map.computeIfAbsent(String.valueOf(20),new AbsentFunciton()); //如果不存在添加 System.out.println(map);map.compute(String.valueOf(8),new MyFunction()); //如果存在則計(jì)算,不存在添加 System.out.println(map); }//上面要使用的接口實(shí)現(xiàn) class MyFunction implements BiFunction {@Overridepublic Object apply(Object key, Object oldValue) {if (key.equals("5")) {return (Integer)oldValue + 3;}return oldValue;} }class AbsentFunciton implements Function{@Overridepublic Object apply(Object key) {return key;} }

下面對3個(gè)方法進(jìn)行一下介紹

computeIfAbsent

根據(jù)給定的key 在hashtable中查找,如果找到了返回key對應(yīng)的值,如果沒找到,根據(jù)定義的計(jì)算功能,算出新值,如果新值不為空,添加到hashtable中 public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {//計(jì)算功能不能為空Objects.requireNonNull(mappingFunction);// 緩存內(nèi)部tableEntry<?,?> tab[] = table;// 根據(jù)給定的key 計(jì)算出hashint hash = key.hashCode();// 根據(jù)hash求出在數(shù)組第幾個(gè)鏈上int index = (hash & 0x7FFFFFFF) % tab.length;@SuppressWarnings("unchecked")Entry<K,V> e = (Entry<K,V>)tab[index];// 如果在鏈表中找到,則返回舊值for (; e != null; e = e.next) {if (e.hash == hash && e.key.equals(key)) {// Hashtable not accept null valuereturn e.value;}}// 記錄modCount 在計(jì)算時(shí),不允許修改hashtable結(jié)構(gòu)int mc = modCount;// 獲得根據(jù)計(jì)算功能計(jì)算出的新值V newValue = mappingFunction.apply(key);if (mc != modCount) { throw new ConcurrentModificationException(); }// 如果新值不為空,添加到hashtable中if (newValue != null) {addEntry(hash, key, newValue, index);}// 返回新值return newValue; }

computeIfPresent

根據(jù)給定的key在hashtable中查找,如果沒找到,返回空,如果找到了,根據(jù)定義的功能,計(jì)算出新值,如果新值為 null,則將key對應(yīng)的節(jié)點(diǎn)刪除,如果不是空,更新節(jié)點(diǎn)值,最后返回新值。 public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {//定義的計(jì)算功能不能為空Objects.requireNonNull(remappingFunction);//復(fù)制一份tableEntry<?,?> tab[] = table;// 根據(jù)hash計(jì)算在hashtable中的位置int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length;@SuppressWarnings("unchecked")Entry<K,V> e = (Entry<K,V>)tab[index];// 在對應(yīng)的位置的鏈表中查找for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {if (e.hash == hash && e.key.equals(key)) {// 如果找到了,根據(jù)key、舊值和定義的功能計(jì)算出新值 int mc = modCount;V newValue = remappingFunction.apply(key, e.value);if (mc != modCount) {throw new ConcurrentModificationException();}// 要將新值賦值到原來的key上,如果新值為空,則要在鏈表上刪除對應(yīng)的節(jié)點(diǎn),計(jì)數(shù)器-1if (newValue == null) {if (prev != null) {prev.next = e.next; } else {tab[index] = e.next;}modCount = mc + 1;count--;} else {e.value = newValue;}// 返回新值return newValue;}}// 如果在對應(yīng)位置上的鏈表中沒有找到,則返回空return null; }

compute

思路就是有就更新,沒有就添加,是上面兩個(gè)的整合。 public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {// 定義的計(jì)算功能不能為空Objects.requireNonNull(remappingFunction);Entry<?,?> tab[] = table;// 根據(jù)hash獲取鏈表位置int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length;@SuppressWarnings("unchecked")Entry<K,V> e = (Entry<K,V>)tab[index];如果根據(jù)key找到了對應(yīng)的節(jié)點(diǎn),更新對應(yīng)的節(jié)點(diǎn)值,并返回根據(jù)功能計(jì)算的新值for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {if (e.hash == hash && Objects.equals(e.key, key)) {int mc = modCount;V newValue = remappingFunction.apply(key, e.value);if (mc != modCount) {throw new ConcurrentModificationException();}if (newValue == null) {if (prev != null) {prev.next = e.next;} else {tab[index] = e.next;}modCount = mc + 1;count--;} else {e.value = newValue;}return newValue;}}// 如果沒有找到根據(jù)key 計(jì)算出新值,如果新值不為空添加到table中,返回計(jì)算的新值int mc = modCount;V newValue = remappingFunction.apply(key, null);if (mc != modCount) { throw new ConcurrentModificationException(); }if (newValue != null) {addEntry(hash, key, newValue, index);}return newValue; }

轉(zhuǎn)載于:https://www.cnblogs.com/arax/p/8206702.html

總結(jié)

以上是生活随笔為你收集整理的HashTable 源码解读的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。