數據存在那?一個用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了。