java --HashMap和HashTable區別

public class HashMap<K,V> extends AbstractMap<K,V>

    implements Map<K,V>, Cloneable, Serializable {}

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {

           public synchronized Enumeration<K> keys() {
                 return this.<K>getEnumeration(KEYS);
           }

          public synchronized Enumeration<V> elements() {
                return this.<V>getEnumeration(VALUES);
          }

}

Hashtable HashMap做爲 Map的基本特性
兩者都實現了Map接口,基本特性相同
-         對同一個Key,只會有一個對應的value值存在
-         如何算是同一個Key? 首先,兩個key對象的hash值相同,其次,key對象的equals方法返回真
內部數據結構
Hashtable和HashMap的內部數據結構相似

 
其基本內部數據結構是一個Entry數組 (transientEntry[] table)
-         數組元素爲實現Map.Entry<K,V>接口的類,Hashtable和HashMap各自實現了自己的Entry類。
-         Entry包含一個Key-value對,以及一個next指針指向另一個Entry。多個Entry可以組成一個單向鏈表。
常用操作
數據插入操作: put(key,value)
-         根據Key的hash值計算出該Entry所應存放的位置(數組下標)
-         若該數組元素爲空,直接放置Entry到此處
-         若多個不同的Key所計算得到的數組下標相同,新加入的Key-value對(Entry)會被加入到Entry單向鏈表中。Hashtable和HashMap都是將其插入鏈表首部.
-         若已經有相同的Key存在於這個鏈表中,則,新的value值會取代老的value
-         當Map中存放的Entry數量超過其限制( 數組長度 * 負荷因子)時,Map將自動重新調整數組大小並重新對Entry進行散列
 
 
數據查找:get(key)
-         根據Key的hash值計算出該Entry對所應存放的位置(數組下標)
-         得到該位置的第一個Entry對象,比較key和Entry.key,若hash值相同,並且equals爲真,則該Entry是我們要找的Key-value對,否則繼續沿next指針構成的單向鏈表查找
 
數據移除:remove(key)
-         按照上述數據查找的方式找到key所在的Entry對象,將其移除,並保持Entry單向鏈表的連通性
Hashtable HashMap 的比較

 

 
Hashtable
HashMap
併發操作
使用同步機制,
實際應用程序中,僅僅是Hashtable本身的同步並不能保證程序在併發操作下的正確性,需要高層次的併發保護。
下面的代碼試圖在key所對應的value值等於x的情況下修改value爲x+1
{
 value = hashTable.get(key);
   if(value.intValue()== x){
hashTable.put(key,      new Integer(value.intValue()+1));
   }
}
如2個線程同時執行以上代碼,可能放入不是x+1,而是x+2.
沒有同步機制,需要使用者自己進行併發訪問控制
數據遍歷的方式
Iterator 和 Enumeration
Iterator
是否支持fast-fail
用Iterator遍歷,支持fast-fail
用Enumeration不支持fast-fail.
支持fast-fail
是否接受值爲null的Key 或Value?
不接受
接受
根據hash值計算數組下標的算法
當數組長度較小,並且Key的hash值低位數值分散不均勻時,不同的hash值計算得到相同下標值的機率較高
 
hash = key.hashCode();
index=(hash&0x7FFFFFFF) % tab.length;
優於hashtable,通過對Key的hash做移位運算和位的與運算,使其能更廣泛地分散到數組的不同位置
 
hash = hash (k);
index = indexFor(hash, table.length);
 
static int hash(Object x) {
 int h = x.hashCode();
h += ~(h << 9);
 h ^= (h >>> 14);
  h += (h << 4);
 h ^= (h >>> 10);
 return h;
}
static int indexFor(int h, int length) {
return h & (length-1);
}
 
Entry數組的長度
Ø        缺省初始長度爲11,
Ø        初始化時可以指定initial capacity
Ø        缺省初始長度爲16,
Ø        長度始終保持2的n次方
Ø        初始化時可以指定initial capacity,若不是2的次方,HashMap將選取第一個大於initial capacity 的2n次方值作爲其初始長度
LoadFactor負荷因子
0.75
負荷超過(loadFactor * 數組長度)時,內部數據的調整方式
擴展數組:2*原數組長度+1
擴展數組: 原數組長度 * 2
兩者都會重新根據Key的hash值計算其在數組中的新位置,重新放置。算法相似,時間、空間效率相同
 
一般情況下,HashMap能夠比Hashtable工作的更好、更快,主要得益於它的散列算法,以及沒有同步。應用程序一般在更高的層面上實 現了保護機制,而不是依賴於這些底層數據結構的同步,因此,HashMap能夠在大多應用中滿足需要。推薦使用HashMap,如果需要同步,可以使用同 步工具類將其轉換成支持同步的HashMap。
Map的效率
Map的效率與Entry數組大小及負荷因子的選取有密切關係。選取適當的數組大小有利於Key-value對的散列分佈,並且,如果數組足夠 大,將有效的減少重新調整數組的次數,提高效率。較小的負荷因子將佔用更多的空間,但降低衝突的可能性,從而將加快訪問和更新的速度。
另外,Key的hash值本身如果能保證較好的散列性,也有益於提高Map的讀寫效率。在effective java中,對hash()的重載有好的建議。
關於如何提高Map的執行效率,可參考《Java Map 集合類簡介》http://www.oracle.com/technology/global/cn/pub/articles/maps1.html
 
辨析
 “Hashtable和HashMap的區別主要是前者是同步的,後者是快速失敗機制保證不會出現多線程併發錯誤(Fast-Fail)。”,這是一個被很多文章轉載過的概念,但其描述並不準確,容易引起誤會。
實質上,Fast-fail與同步保護的是兩種不同情況下的併發,兩者不能拿來做比較。
Hashtable是同步的,在執行get,put,remove,size,clear等一次性讀寫操作時,使用了同步機制,避免了多個線程 同時讀寫Hashtable。但同步機制並不能避免在iterator或Enumeration遍歷過程中其他線程對Hashtable的put、 remove、clear操作,這些寫操作都會被毫無阻攔得成功執行。
快速失敗機制主要目的在於使iterator遍歷數組的線程能及時發現其他線程對Map的修改(如put、remove、clear等),因 此,fast-fail並不能保證所有情況下的多線程併發錯誤,只能保護iterator遍歷過程中的iterator.next()與寫併發.
其次,Hashtable的iterator遍歷方式也是支持fast-fail的,不能說它沒有快速失敗機制。

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