Java中的Hashtable源碼詳細(xì)解析
1、簡(jiǎn)介
和HashMap一樣,Hashtable 也是一個(gè)散列表,它存儲(chǔ)的內(nèi)容是鍵值對(duì)(key-value)映射。
Hashtable 繼承于Dictionary,實(shí)現(xiàn)了Map、Cloneable、java.io.Serializable接口。 Hashtable 的函數(shù)都是同步的,這意味著它是線程安全的。它的key、value都不可以為null。此外,Hashtable中的映射不是有序的。
Hashtable 的實(shí)例有兩個(gè)參數(shù)影響其性能:初始容量 和 加載因子。
容量:是哈希表中桶 的數(shù)量,初始容量 就是哈希表創(chuàng)建時(shí)的容量。注意,哈希表的狀態(tài)為 open:在發(fā)生“哈希沖突”的情況下,單個(gè)桶會(huì)存儲(chǔ)多個(gè)條目,這些條目必須按順序搜索。
加載因子:是對(duì)哈希表在其容量自動(dòng)增加之前可以達(dá)到多滿的一個(gè)尺度(閾值)。初始容量和加載因子這兩個(gè)參數(shù)只是對(duì)該實(shí)現(xiàn)的提示。關(guān)于何時(shí)以及是否調(diào)用 rehash 方法的具體細(xì)節(jié)則依賴于該實(shí)現(xiàn)。
通常,默認(rèn)加載因子是 0.75, 這是在時(shí)間和空間成本上尋求一種折衷。加載因子過(guò)高雖然減少了空間開銷,但同時(shí)也增加了查找某個(gè)條目的時(shí)間(在大多數(shù) Hashtable 操作中,包括 get 和 put 操作,都反映了這一點(diǎn))。
2、關(guān)鍵點(diǎn)
- Hashtable繼承于Dictionary類,實(shí)現(xiàn)了Map接口。Map是”key-value鍵值對(duì)”接口,Dictionary是聲明了操作”鍵值對(duì)”函數(shù)接口的抽象類。
- Hashtable是通過(guò)”拉鏈法“實(shí)現(xiàn)的哈希表。它包括幾個(gè)重要的成員變量:table, count, threshold, loadFactor, modCount。
- table是一個(gè)Entry[]數(shù)組類型,而Entry實(shí)際上就是一個(gè)單向鏈表。哈希表的”key-value鍵值對(duì)”都是存儲(chǔ)在Entry數(shù)組中的。
- count是Hashtable的大小,它是Hashtable保存的鍵值對(duì)的數(shù)量。
- threshold是Hashtable的閾值,用于判斷是否需要調(diào)整Hashtable的容量。threshold的值=”容量*加載因子”。
- loadFactor就是加載因子。
- modCount是用來(lái)實(shí)現(xiàn)fail-fast機(jī)制的。
3、Hashtable數(shù)據(jù)存儲(chǔ)數(shù)組
private transient Entry<?,?>[] table;
Hashtable中的key-value都是存儲(chǔ)在table數(shù)組中的。
4、數(shù)據(jù)節(jié)點(diǎn)Entry<K,V>的數(shù)據(jù)結(jié)構(gòu)
private static class Entry<K,V> implements Map.Entry<K,V> { final int hash; //hash值 final K key; //鍵 V value; //值 Entry<K,V> next; //鏈表中下一個(gè)Entry的引用 //Entry的構(gòu)造函數(shù) protected Entry(int hash, K key, V value, Entry<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } @SuppressWarnings("unchecked") protected Object clone() { return new Entry<>(hash, key, value, (next==null ? null : (Entry<K,V>) next.clone())); } // Map.Entry Ops public K getKey() { return key; } public V getValue() { return value; } //設(shè)置value。若value是null,則拋出異常。 public V setValue(V value) { if (value == null) throw new NullPointerException(); V oldValue = this.value; this.value = value; return oldValue; } // 覆蓋equals()方法,判斷兩個(gè)Entry是否相等。 // 若兩個(gè)Entry的key和value都相等,則認(rèn)為它們相等。 public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>)o; return (key==null ? e.getKey()==null : key.equals(e.getKey())) && (value==null ? e.getValue()==null : value.equals(e.getValue())); } //Entry對(duì)象的hash值 public int hashCode() { return hash ^ Objects.hashCode(value); } public String toString() { return key.toString()+"="+value.toString(); } }
5、Hashtable的構(gòu)造函數(shù)
// 指定“容量大小”和“加載因子”的構(gòu)造函數(shù) public Hashtable(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load: "+loadFactor); if (initialCapacity==0) initialCapacity = 1; this.loadFactor = loadFactor; table = new Entry<?,?>[initialCapacity]; threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); } // 指定“容量大小”的構(gòu)造函數(shù),加載因子使用默認(rèn)值0.75 public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); } // 默認(rèn)構(gòu)造函數(shù),指定的容量大小是11,加載因子是0.75 public Hashtable() { this(11, 0.75f); } // 包含“子Map”的構(gòu)造函數(shù) public Hashtable(Map<? extends K, ? extends V> t) { this(Math.max(2*t.size(), 11), 0.75f); // 將“子Map”的全部元素都添加到Hashtable中 putAll(t); }
6、Hashtable的主要對(duì)外接口
6.1 clear()
clear() 的作用是清空Hashtable。它是將Hashtable的table數(shù)組的值全部設(shè)為null。
public synchronized void clear() { Entry<?,?> tab[] = table; modCount++; for (int index = tab.length; --index >= 0; ) tab[index] = null; //將數(shù)組中的引用全部置為null count = 0; }
6.2 contains() 和 containsValue()
contains() 和 containsValue() 的作用都是判斷Hashtable是否包含“值(value)”。
public synchronized boolean contains(Object value) { // Hashtable中“鍵值對(duì)”的value不能是null, // 若是null的話,拋出異常! if (value == null) { throw new NullPointerException(); } Entry<?,?> tab[] = table; // 從后向前遍歷table數(shù)組中的元素(Entry) // 對(duì)于每個(gè)Entry(單向鏈表),逐個(gè)遍歷,判斷節(jié)點(diǎn)的值是否等于value for (int i = tab.length ; i-- > 0 ;) { for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) { if (e.value.equals(value)) { return true; } } } return false; }
public boolean containsValue(Object value) { return contains(value); }
6.3 containsKey()
containsKey() 的作用是判斷Hashtable是否包含key。
public synchronized boolean containsKey(Object key) { Entry<?,?> tab[] = table; int hash = key.hashCode(); // 計(jì)算索引值, // % tab.length 的目的是防止數(shù)據(jù)越界 int index = (hash & 0x7FFFFFFF) % tab.length; // 找到“key對(duì)應(yīng)的Entry(鏈表)”,然后在鏈表中找出“哈希值”和“鍵值”與key都相等的元素 for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return true; } } return false; }
6.4 elements()
elements() 的作用是返回“所有value”的枚舉對(duì)象。
public synchronized Enumeration<V> elements() { return this.<V>getEnumeration(VALUES); }
// 獲取Hashtable的枚舉類對(duì)象 private <T> Enumeration<T> getEnumeration(int type) { if (count == 0) { return Collections.emptyEnumeration(); } else { return new Enumerator<>(type, false); } }
7、put()和get()操作
7.1 put()操作
put() 的作用是對(duì)外提供接口,讓Hashtable對(duì)象可以通過(guò)put()將“key-value”添加到Hashtable中。
public synchronized V put(K key, V value) { // Hashtable中不能插入value為null的元素?。。? if (value == null) { throw new NullPointerException(); } // 若“Hashtable中已存在鍵為key的鍵值對(duì)”, // 則用“新的value”替換“舊的value” Entry<?,?> tab[] = table; int hash = key.hashCode(); //計(jì)算key的索引 int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; for(; entry != null ; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } //添加Entry addEntry(hash, key, value, index); return null; }
private void addEntry(int hash, K key, V value, int index) { //將“修改統(tǒng)計(jì)數(shù)”+1 modCount++; Entry<?,?> tab[] = table; //若“Hashtable實(shí)際容量” > “閾值”(閾值=總的容量 * 加載因子) //則調(diào)整Hashtable的大小 if (count >= threshold) { // Rehash the table if the threshold is exceeded // rehash(); tab = table; hash = key.hashCode(); index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. //將“Hashtable中index”位置的Entry(鏈表)保存到e中 @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) tab[index]; //采用 “頭插法” //創(chuàng)建“新的Entry節(jié)點(diǎn)”,并將“新的Entry”插入“Hashtable的index位置”, //并設(shè)置e為“新的Entry”的下一個(gè)元素(即“新Entry”為鏈表表頭)。 tab[index] = new Entry<>(hash, key, value, e); //將“Hashtable的實(shí)際容量”+1 count++; }
7.2 get()操作
get() 的作用就是獲取key對(duì)應(yīng)的value,沒(méi)有的話返回null。
public synchronized V get(Object key) { Entry<?,?> tab[] = table; //key的hash值 int hash = key.hashCode(); //計(jì)算索引 int index = (hash & 0x7FFFFFFF) % tab.length; //找到“key對(duì)應(yīng)的Entry(鏈表)”,然后在鏈表中找出“哈希值”和“鍵值”與key都相等的元素 for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return (V)e.value; } } return null; }
8、rehash()
rehash()函數(shù)負(fù)責(zé)hashtable的擴(kuò)容。
protected void rehash() { int oldCapacity = table.length; //擴(kuò)容前的map Entry<?,?>[] oldMap = table; // overflow-conscious code ////新容量 = 舊容量 * 2 + 1 int newCapacity = (oldCapacity << 1) + 1; if (newCapacity - MAX_ARRAY_SIZE > 0) { if (oldCapacity == MAX_ARRAY_SIZE) // Keep running with MAX_ARRAY_SIZE buckets return; newCapacity = MAX_ARRAY_SIZE; } //創(chuàng)建新的數(shù)組 Entry<?,?>[] newMap = new Entry<?,?>[newCapacity]; modCount++; //更新 新的 擴(kuò)容閾值 threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); table = newMap; //遍歷舊的數(shù)組,將所有的鍵值對(duì)Entry移動(dòng)到新數(shù)組中 //注意,移動(dòng)的是對(duì)象的引用 for (int i = oldCapacity ; i-- > 0 ;) { for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) { Entry<K,V> e = old; old = old.next; //計(jì)算在新數(shù)組中的索引 int index = (e.hash & 0x7FFFFFFF) % newCapacity; //采用“頭插法” e.next = (Entry<K,V>)newMap[index]; newMap[index] = e; } } }
9、remove()
remove() 的作用就是刪除Hashtable中鍵為key的元素。
public synchronized V remove(Object key) { Entry<?,?> tab[] = table; int hash = key.hashCode(); //計(jì)算索引 int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>)tab[index]; // 找到“key對(duì)應(yīng)的Entry(鏈表)” // 然后在鏈表中找出要?jiǎng)h除的節(jié)點(diǎn),并刪除該節(jié)點(diǎn)。 for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { modCount++; if (prev != null) { //鏈表刪除節(jié)點(diǎn)的方式 prev.next = e.next; } else { tab[index] = e.next; } count--; V oldValue = e.value; //將value引用置為null,回收value對(duì)象 e.value = null; return oldValue; } } return null; }
到此這篇關(guān)于Java中的Hashtable源碼詳細(xì)解析的文章就介紹到這了,更多相關(guān)Hashtable源碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot RestTemplate 簡(jiǎn)單使用解析
這篇文章主要介紹了Springboot RestTemplate 簡(jiǎn)單使用解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08Spring中校驗(yàn)器(Validator)的深入講解
Spring校驗(yàn)器,參數(shù)校驗(yàn)從此簡(jiǎn)單。下面這篇文章主要給大家介紹了關(guān)于Spring中校驗(yàn)器(Validator)的相關(guān)資料,文中通過(guò)示例代碼介紹非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06使用maven創(chuàng)建普通項(xiàng)目命令行程序詳解
大部分使用maven創(chuàng)建的是web項(xiàng)目,這里使用maven創(chuàng)建一個(gè)命令行程序,目的是讓大家了解maven特點(diǎn)和使用方式,有需要的朋友可以借鑒參考下2021-10-10java開發(fā)SSM框架具有rest風(fēng)格的SpringMVC
這篇文章主要介紹了java開發(fā)中如何使SSM框架具有rest風(fēng)格的SpringMVC實(shí)現(xiàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-10-10Java中byte輸出write到文件的實(shí)現(xiàn)方法講解
今天小編就為大家分享一篇關(guān)于Java中byte輸出write到文件的實(shí)現(xiàn)方法講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03Spring Data JPA 如何使用QueryDsl查詢并分頁(yè)
這篇文章主要介紹了Spring Data JPA 如何使用QueryDsl查詢并分頁(yè),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11解決resultMap映射數(shù)據(jù)錯(cuò)誤的問(wèn)題
這篇文章主要介紹了解決resultMap映射數(shù)據(jù)錯(cuò)誤的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08