【java源碼一帶一路系列】之Hashtable

從上往下看的源碼,標註了路上一些「景點」。皮皮蝦,咱們走。
/**
 * 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."(唱起來了) 我,筆,蘋果是三個對象,我引用了筆和蘋果,此時對我進行拷貝,效果如圖:
image

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>,&nbsp;</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;

相關文章
相關標籤/搜索