摘要:從上往下看的源碼,標注了路上一些景點。感興趣的同學可以繼續跟,它返回的是一個內部實現的類實現了。此外,循環中的新舊數據遷移的行代碼也很經典。一詞成功引起了筆者注意。與之對應的是深拷貝。里面的對象會在原來的對象和它的副本之間共享。
從上往下看的源碼,標注了路上一些“景點”。皮皮蝦,我們走。
/** * Constructs a new hashtable with the same mappings as the given * Map. The hashtable is created with an initial capacity sufficient to * hold the mappings in the given Map and a default load factor (0.75). * * @param t the map whose mappings are to be placed in this map. * @throws NullPointerException if the specified map is null. * @since 1.2 */ public Hashtable(Map extends K, ? extends V> t) { this(Math.max(2*t.size(), 11), 0.75f); putAll(t); }
initialCapacity不指定的話是默認11;
這里多帶帶列出這個初始化方法,主要是因為這個2*t.size(),如果你還記得的話,在第二路文中介紹HashMap.resize()時,它就是按2擴容的。
/** * Returns an enumeration of the keys in this hashtable. * * @return an enumeration of the keys in this hashtable. * @see Enumeration * @see #elements() * @see #keySet() * @see Map */ public synchronized Enumerationkeys() { return this. getEnumeration(KEYS); } // Types of Enumerations/Iterations private static final int KEYS = 0; private static final int VALUES = 1; private static final int ENTRIES = 2;
看到synchronized想說的是HashTable是同步,所以大多數方法都可見進行了同步處理,這點與HashMap不同(其他差別:HashTable的key、value都不允許為null)。該方法通過Enumeration遍歷Hashtable的鍵(KEYS是定義的全局變量),類似的,還有通過Enumeration遍歷Hashtable的值。感興趣的同學可以繼續跟getEnumeration(),它返回的是一個hashtable內部實現的Enumerator類(實現了Enumeration, Iterator)。
應用實例如下:
Enumeration enu = table.keys(); while(enu.hasMoreElements()) { System.out.println(enu.nextElement()); }
/** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
這里好奇的是“-8”從何而來?從stackoverflow得到的答案是(譯):
數組(ARRAY)需要用8bytes來存儲(2^31 = 2,147,483,648 )大小(size)。
/** * Increases the capacity of and internally reorganizes this * hashtable, in order to accommodate and access its entries more * efficiently. This method is called automatically when the * number of keys in the hashtable exceeds this hashtable"s capacity * and load factor. */ @SuppressWarnings("unchecked") protected void rehash() { int oldCapacity = table.length; Entry,?>[] oldMap = table; // overflow-conscious code 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; } Entry,?>[] newMap = new Entry,?>[newCapacity]; modCount++; threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); table = newMap; for (int i = oldCapacity ; i-- > 0 ;) { for (Entryold = (Entry )oldMap[i] ; old != null ; ) { Entry e = old; old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = (Entry )newMap[index]; newMap[index] = e; } } }
hashtable的新容量是:2倍+1,即始終為奇數。不同于前2回講過的hashmap中resize()則是2倍。why?我們知道“除2以外所有的素數都是奇數”,而當哈希表的大小為素數時,簡單的取模哈希的結果分布更加均勻,從而降低哈希沖突。
“0x7FFFFFFF”即二進制的32個1,按位與運算后使得結果范圍在區間[0,2147483647]內,可以理解為取正。
此外,for循環中的新舊數據遷移的5行代碼也很經典。
/** * Creates a shallow copy of this hashtable. All the structure of the * hashtable itself is copied, but the keys and values are not cloned. * This is a relatively expensive operation. * * @return a clone of the hashtable */ public synchronized Object clone() { try { Hashtable,?> t = (Hashtable,?>)super.clone(); t.table = new Entry,?>[table.length]; for (int i = table.length ; i-- > 0 ; ) { t.table[i] = (table[i] != null) ? (Entry,?>) table[i].clone() : null; } t.keySet = null; t.entrySet = null; t.values = null; t.modCount = 0; return t; } catch (CloneNotSupportedException e) { // this shouldn"t happen, since we are Cloneable throw new InternalError(e); } }
“shallow copy”一詞成功引起了筆者注意。what is it?淺拷貝非java獨有的概念,其他編程語言同樣存在,如C#、C++、IOS、python等。與之對應的是深拷貝(deep copy)。兩者的區別是:
對象的淺拷貝會對“主”對象進行拷貝,但不會復制主對象里面的對象。"里面的對象“會在原來的對象和它的副本之間共享。深拷貝是一個整個獨立的對象拷貝。[參考文獻2]
“I have a pen, I have a apple."(唱起來了) 我,筆,蘋果是三個對象,我引用了筆和蘋果,此時對我進行拷貝,效果如圖:
public class Apple { String color; public String getColor() { return color; } public void setColor(String color) { this.color = color; } } Apple apple = new Apple(); apple.setColor("red"); Hashtable ht = new Hashtable(); ht.put("apple", apple); System.out.println(((Apple)ht.get("apple")).getColor()); Hashtable htc = (Hashtable) ht.clone(); System.out.println(((Apple)htc.get("apple")).getColor()); ((Apple)htc.get("apple")).setColor("green"); System.out.println(((Apple)ht.get("apple")).getColor()); System.out.println(((Apple)htc.get("apple")).getColor()); //輸出結果: //red //red //green //green //淺拷貝的hashtable共用一個蘋果
/** * Returns a string representation of this Hashtable object * in the form of a set of entries, enclosed in braces and separated * by the ASCII characters ", " (comma and space). Each * entry is rendered as the key, an equals sign =, and the * associated element, where the toString method is used to * convert the key and element to strings. * * @return a string representation of this hashtable */ public synchronized String toString() { int max = size() - 1; if (max == -1) return "{}"; StringBuilder sb = new StringBuilder(); Iterator> it = entrySet().iterator(); sb.append("{"); for (int i = 0; ; i++) { Map.Entry e = it.next(); K key = e.getKey(); V value = e.getValue(); sb.append(key == this ? "(this Map)" : key.toString()); sb.append("="); sb.append(value == this ? "(this Map)" : value.toString()); if (i == max) return sb.append("}").toString(); sb.append(", "); } }
"(this Map)",即,ht.put(ht, ht)將輸出{(this Map)=(this Map)}。
還有同步處理用到的volatile關鍵字(各個線程在訪問該關鍵字所修飾變量前必須從主存(共享內存)中讀取該變量的值)、Collections.synchronizedSet()(創建同步的集合對象)有緣再續。
更多有意思的內容,歡迎訪問筆者小站: rebey.cn
參考文獻[推薦]:1.HashMap和HashTable到底哪不同?,2016-07-05;
2.Java 中的淺拷貝與深拷貝(Shallow vs. Deep Copy in Java ),中英;
3.Java基礎之volatile關鍵字,2016-07-18;
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67138.html
摘要:如今行至于此,當觀賞一方。由于所返回的無執行意義。源碼閱讀總體門檻相對而言比,畢竟大多數底層都由實現了。比心可通過這篇文章理解創建一個實例過程圖工作原理往期線路回顧源碼一帶一路系列之源碼一帶一路系列之源碼一帶一路系列之 本文以jdk1.8中LinkedHashMap.afterNodeAccess()方法為切入點,分析其中難理解、有價值的源碼片段(類似源碼查看是ctrl+鼠標左鍵的過程...
摘要:一路至此,風景過半。與雖然名字各異,源碼實現基本相同,除了增加了線程安全。同時注意溢出情況處理。同時增加了考慮并發問題。此外,源碼中出現了大量泛型如。允許為非線程安全有序。 一路至此,風景過半。ArrayList與Vector雖然名字各異,源碼實現基本相同,除了Vector增加了線程安全。所以作者建議我們在不需要線程安全的情況下盡量使用ArrayList。下面看看在ArrayList源...
摘要:同樣在源碼的與分別見看到老朋友和。這樣做可以降低性能消耗的同時,還可以減少序列化字節流的大小,從而減少網絡開銷框架中。使用了反射來尋找是否聲明了這兩個方法。十進制和,通過返回值能反應當前狀態。 Map篇暫告段落,卻并非離我們而去。這不在本篇中你就能經常見到她。HashSet、LinkedHashSet、TreeSet各自基于對應Map實現,各自源碼內容較少,因此歸納為一篇。 HashS...
摘要:本篇涉及少許以下簡稱新特性,請驢友們系好安全帶,準備開車。觀光線路圖是在中新增的一個方法,相對而言較為陌生。其作用是把的計算結果關聯到上即返回值作為新。實際上,乃縮寫,即二元函數之意類似。 本文以jdk1.8中HashMap.compute()方法為切入點,分析其中難理解、有價值的源碼片段(類似源碼查看是ctrl+鼠標左鍵的過程)。本篇涉及少許Java8(以下簡稱J8)新特性,請驢友們...
摘要:觀光線路圖將涉及到的源碼全局變量哈希表初始化長度默認值是負載因子默認表示的填滿程度。根據是否為零將原鏈表拆分成個鏈表,一部分仍保留在原鏈表中不需要移動,一部分移動到原索引的新鏈表中。 前言 本文以jdk1.8中HashMap.putAll()方法為切入點,分析其中難理解、有價值的源碼片段(類似ctrl+鼠標左鍵查看的源碼過程)。?觀光線路圖:putAll() --> putMapEnt...
閱讀 2161·2021-10-08 10:15
閱讀 1191·2019-08-30 15:52
閱讀 519·2019-08-30 12:54
閱讀 1536·2019-08-29 15:10
閱讀 2690·2019-08-29 12:44
閱讀 3011·2019-08-29 12:28
閱讀 3356·2019-08-27 10:57
閱讀 2220·2019-08-26 12:24