HashMap和HashTable比較

java集合整體分爲Collection和Map兩種。

java.util包中集合詳解
Java中Collection與Map詳解
Java基礎——集合類 左Collection,右Map
總結一下Collection和Map,它們的父子關係爲:

  java.util
        +Collection 這個接口extends自 --java.lang.Iterable接口
           +List 接口 
              -ArrayList 類  #ArrayList實現了可變大小的數組。
              #它允許所有元素,包括null。ArrayList沒有同步
              #線程不安全的
              -LinkedList 類   #LinkedList沒有同步方法。
              #如果多個線程同時訪問一個List,則必須自己實現訪問同步
              +Vector 類     #此類是實現同步的,線程安全
                - Stack類:繼承自Vector,實現一個後進先出的堆棧

           +Queue 接口
              +不常用,

           +Set 接口  不包含重複的元素的Collection
              +SortedSet 接口
                 -TreeSet 類
                 #TreeSet實現了SortedSet接口,能夠對集合中的對象進行排序
              -HashSet
              #按照哈希算法來存取集合中的對象,存取速度比較快。

        +Map 接口  #Map沒有繼承Collection接口,Map提供key到value的映射
          -HashMap 類 (除了不同步和允許使用 null 鍵/值之外,與 Hashtable 大致相同.)
          -Hashtable 類 此類是實現同步的,不允許使用 null 鍵值
          +SortedMap 接口
             -TreeMap 類

這裏寫圖片描述

下面正式將HashMap和HashTable:

HashMap和Hashtable都實現了Map接口,但使用它們可以根據它們區別合理選擇使用,主要的區別有:線程安全性,同步(synchronization),以及速度。

1.繼承的接口和類不同

public class HashMap extends AbstractMap implements Map, Cloneable, Serializable{}

public class Hashtable extends Dictionary implements Map, Cloneable, java.io.Serializable{}

2.線程安全

HashMap幾乎可以等價於Hashtable,除了HashMap是非synchronized的,HashTable中的方法是同步的。故Hashtable是線程安全的,多個線程可以共享一個Hashtable;而如果沒有正確的同步的話,多個線程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴展性更好。

可以查看HashTable中方法代碼:(做了同步處理)

public synchronized V put(K key,V value) {

// Make sure the value is not null

if(value==null) {

throw newNullPointerException();

}

3、null的處理:

HashMap 可以接受null(HashMap可以接受爲null的鍵值(key)和值(value),而Hashtable則不行),其中只有一個key可以爲null,多個不同的key對應的value可以爲null。判斷是否存在某個key應該用containsKey(key)。

HashTable 的key和value都不能爲null,否則拋出異常NullPointerException

4、遍歷方式略有差異:

HahsMap,Hashtable都可使用Iterator遍歷:

Collectioncollection=map.values();

Iteratorit=collection.iterator();

CollectioncollectionTable=table.values();

IteratoritTable=collectionTable.iterator();

Hashtable還使用了Enumeration的方式:

public synchronized Enumeration elements() {

return this.getEnumeration(VALUES);

}

兩者也都可以通過 entrySet() 方法返回一個 Set , 然後進行遍歷處理:

HashMap:

Set setMap=map.entrySet();

Iterator itMapSet=setMap.iterator();

while(itMapSet.hasNext()) {

Map.Entryentry= (Map.Entry)itMapSet.next();

System.out.println("entry.getKey()  = "+entry.getKey() +", entry.getValue() = "+entry.getValue() );

}

HashTable:

Set setTable=table.entrySet();

HashMap的迭代器(Iterator)是fail-fast迭代器(java.util 包中的集合類都返回fail-fast 迭代器,這意味着它們假設線程在集合內容中進行迭代時,集合不會更改它的內容。如果fail-fast 迭代器檢測到在迭代過程中進行了更改操作,那麼它會拋出ConcurrentModificationException ,這是不可控異常),而Hashtable的enumerator迭代器不是fail-fast的。所以當有其它線程改變了HashMap的結構(增加或者移除元素),將會拋出ConcurrentModificationException,但迭代器本身的remove()方法移除元素則不會拋出ConcurrentModificationException異常。但這並不是一個一定發生的行爲,要看JVM。這條同樣也是Enumeration和Iterator的區別。

5、哈希值的使用不同

Hashtable直接使用對象的hashCode:

int hash=key.hashCode();

而HashMap重新計算hash值:

static final int hash(Objectkey) {

int h;

return(key==null) ?0: (h=key.hashCode()) ^ (h>>>16);

}

6、Hashtable中hash數組默認大小是11,增加的方式是 old*2+1。HashMap中hash數組的默認大小是16,而且一定是2的指數。

why? :
因爲:在hashmap的源碼中。put方法會調用indexFor(int h, int length)方法,這個方法主要是根據key的hash值找到這個entry在table中的位置,源碼如下:/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 : “length must be a non-zero power of 2”;
return h & (length-1);
}
注意最後return的是h&(length-1)。如果length不爲2的冪,比如15。那麼length-1的2進制就會變成1110。在h爲隨機數的情況下,和1110做&操作。尾數永遠爲0。那麼0001、1001、1101等尾數爲1的位置就永遠不可能被entry佔用。這樣會造成浪費,不隨機等問題。

注意:

1) sychronized意味着在一次僅有一個線程能夠更改Hashtable。就是說任何線程要更新Hashtable時要首先獲得同步鎖,其它線程要等到同步鎖被釋放之後才能再次獲得同步鎖更新Hashtable。

2) Fail-safe和iterator迭代器相關。如果某個集合對象創建了Iterator或者ListIterator,然後其它的線程試圖“結構上”更改集合對象,將會拋出ConcurrentModificationException異常。但其它線程可以通過set()方法更改集合對象是允許的,因爲這並沒有從“結構上”更改集合。但是假如已經從結構上進行了更改,再調用set()方法,將會拋出IllegalArgumentException異常。

3) 結構上的更改指的是刪除或者插入一個元素,這樣會影響到map的結構。

參考鏈接

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