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的結構。