讀Java 11 源碼(4)Hashtable

數據存在那?一個用Entry爲元素的數組。

private transient Entry<?,?>[] table;

table,這個是由Entry這個內部類組成的數組,我們來看看這個類的代碼。

private static class Entry<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
	//(壹)
    Entry<K,V> next;

    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;
    }

    public V setValue(V value) {
        if (value == null)
            throw new NullPointerException();

        V oldValue = this.value;
        this.value = value;
        return oldValue;
    }

    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()));
    }

    public int hashCode() {
        return hash ^ Objects.hashCode(value);
    }

    public String toString() {
        return key.toString()+"="+value.toString();
    }
}

這裏代碼挺多,但是值得看的沒幾個,首先就是在(壹)的聲明,可以看出,這個帶着next節點的,可以組成一個鏈表。

還有就是equals方法的代碼:

return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
	(value==null ? e.getValue()==null : value.equals(e.getValue()));

可以看出,key和value都要進行equals。

如果只是put() 操作,會導致modcount的更改麼?

這個需呀看情況哦,如果當前的key已經存在了,值得是有個index的相同,對象可以equals的key,已經在table裏了。那麼就會執行put操作,(主要還是替換value)。
如果沒有,就是hashcode不同,這種,那麼要修改modCount。

Hashtable 爲什麼是線程安全的?

因爲每個操作都用synchronized這個修飾了,這個同步的操作是由JVM完成的,至於說它到底是怎麼實現的,我們這裏不做深究,這個值得重新在寫一篇來專門的探討探討,比如put()操作:

public synchronized V put(K key, V value) {
    // Make sure the value is not null
  if (value == null) {
        throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
  Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    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;
        }
    }

    addEntry(hash, key, value, index);
    return null;
}

之所以是線程安全的,因爲當多個線程調用,希望去同時修改(比如put,remove這些)這個對象的時候,因爲這些方法被synchronized這個關鍵詞修飾,所以每次只能有一個線程得到修改這個對象的權限,其他的線程只能等待。這樣就保證了線程安全。

優點

線程安全,這個就是這個類唯一的優點了

缺點

性能不行,在高併發的環境下,對象只能每次被一個類修改,如果在大量的併發量的前提下,會導致大量的線程進入等待,很消耗資源,線程之間的頻繁切換很消耗CPU的資源。我們現在一般都是多核處理器,這種處理方式依然是單核來解決這種高併發的問題,如果可以多個線程同時操作一個對象,而且不會有線程安全問題,這樣方案無疑就很nice了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章