從上往下看的源碼,標註了路上一些「景點」。皮皮蝦,咱們走。
/** * 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;html
這裏單獨列出這個初始化方法,主要是由於這個2*t.size(),若是你還記得的話,在第二路文中介紹HashMap.resize()時,它就是按2擴容的。java
/** * 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 Enumeration<K> keys() { return this.<K>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)。
應用實例以下:python
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 (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) { Entry<K,V> e = old; old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = (Entry<K,V>)newMap[index]; newMap[index] = e; } } }
hashtable的新容量是:2倍+1,即始終爲奇數。不一樣於前2回講過的hashmap中resize()則是2倍。why?咱們知道「除2之外全部的素數都是奇數」,而當哈希表的大小爲素數時,簡單的取模哈希的結果分佈更加均勻,從而下降哈希衝突。app
「0x7FFFFFFF」即二進制的32個1,按位與運算後使得結果範圍在區間[0,2147483647]內,能夠理解爲取正。編程語言
此外,for循環中的新舊數據遷移的5行代碼也很經典。ui
/** * 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)。二者的區別是:this
對象的淺拷貝會對「主」對象進行拷貝,但不會複製主對象裏面的對象。"裏面的對象「會在原來的對象和它的副本之間共享。深拷貝是一個整個獨立的對象拷貝。[參考文獻2]spa
「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 <tt>Hashtable</tt> object * in the form of a set of entries, enclosed in braces and separated * by the ASCII characters "<tt>, </tt>" (comma and space). Each * entry is rendered as the key, an equals sign <tt>=</tt>, and the * associated element, where the <tt>toString</tt> 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<Map.Entry<K,V>> it = entrySet().iterator(); sb.append('{'); for (int i = 0; ; i++) { Map.Entry<K,V> 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;