Java容器深入研究
1、完整的容器分類法
2、可選操作
執行各種不同的新增和移除的方法在Collection介面中都是可選操作。
Arrays.asList()會生成一個固定尺寸的List,僅支援那些不會改變陣列大小的操作,如set操作。若執行會改變大小的操作,會丟擲UnsupportedOperationException異常,例如retainAll()、addAll()等。
Collections.unmodifiableList()會生成一個不可修改的List,只要你執行任何試圖修改容器的操作,都會丟擲UnsupportedOperationException異常,例如set()、add()等。
3、Set和儲存順序
(1)Set型別
型別 | 說明 |
---|---|
Set(interface) |
存入Set的每個元素必須是唯一的,因為Set不儲存重複元素。 加入Set的元素必須定義equals()方法以確保物件的唯一性。 Set與Collection有完全一樣的介面。Set介面不保證維護元素的次序。 |
HashSet | 為快速查詢而設計的Set。存入HashSet的元素必須定義hashCode() |
TreeSet |
保持次序的Set,底層為樹結構。使用它可以從Set中提取有序的序列。 元素必須實現Comparable介面。 |
LinkedHashSet |
具有HashSet的查詢速度,且內部使用連結串列維護元素的順序(插入的次序)。 於是在使用迭代器遍歷Set時,結果會按元素插入的次序顯示。 元素也必須定義hashCode()方法。 |
(2)SortedSet
SortedSet中的元素可以保證處於排序狀態。
方法 | 說明 |
---|---|
Object first() | 返回容器中的第一個元素。 |
Object last() | 返回容器中的最末一個元素。 |
SortedSet subSet(fromElement,toElement) | 生成此Set的子集,範圍從fromElement(包含)到toElement(不包含)。 |
SortedSet headSet(toElement) | 生成此Set的子集,由小於toElement的元素組成。 |
SortedSet tailSet(fromElement) | 生成此Set的子集,由大於或等於fromElement的元素組成。 |
4、佇列
佇列可以將元素從佇列的一段插入,並於另一端將它們抽取出來。
(1)優先順序佇列(PriorityQueue)
優先順序佇列會對元素進行排序,排序順序可以通過實現Comparable而進行控制。
(2)雙向佇列
雙向佇列可以在任何一端新增或移除元素。
1 public class Deque<T>{
2 private LinkedList<T> deque = new LinkedList<T>();
3 public void addFirst(T e){ deque.addFirst(e); }
4 public void addLast(T e){ deque.addLast(e); }
5 public T getFirst(){ return deque.getFirst(); }
6 public T getLast(){ return deque.getLast(); }
7 public T removeFirst(){ return deque.removeFirst(); }
8 public T removeLast(){ return deque.removeLast(); }
9 public int size(){ return deque.size(); }
10
11 @Override
12 public String toString() { return deque.toString(); }
13 }
5、Map
Map儲存的是對映表,即它維護的是鍵-值對,因此可以使用鍵來查詢值。基本實現型別有:HashMap、TreeMap,LinkedHashMap、WeakHashMap、ConcurrentHashMap、IdentityHashMap。
(1)效能
HashMap使用雜湊碼來取代對鍵的緩慢搜尋,能顯著提高在搜尋方面的效能。
型別 | 說明 |
---|---|
HashMap |
Map基於散列表的實現(它取代了HashTable)。插入和查詢“鍵值對”的開銷是固定的。 可以通過構造器設定容量和負載因子,以調整容器效能。 |
LinkedHashMap |
類似於HashMap。 迭代遍歷它時,取得“鍵值對”的順序是其插入次序,或者是最近最少使用(LRU)的次序。 只比HashMap慢一點,而在迭代訪問時反而更快,因為它使用連結串列維護內部次序。 |
TreeMap |
基於紅黑樹的實現。檢視“鍵”或“鍵值對”時,它們會被排序(次序由Comparable或Comparator決定)。 TreeMap的特點在於,所得到結果是經過排序的。 TreeMap是唯一的帶有subMap()方法的Map,它可以返回一個子樹。 |
WeakHashMap |
弱鍵(weak key)對映,允許釋放對映所指向的物件;這是為解決某類特殊問題而設計的。 如果對映之外沒有引用指向某個“鍵”,則此“鍵”可以被垃圾收集器回收。 |
ConcurrentHashMap | 一種執行緒安全的Map,它不涉及同步加鎖。 |
IdentityHashMap | 使用==代替equals()對“鍵”進行比較的雜湊對映。專為解決特殊問題而設計的。 |
(2)SortedMap
使用SortedMap可以確保鍵處於排序狀態。
方法 | 說明 |
---|---|
T firstKey() | 返回Map中的第一個鍵。 |
T lastKey() | 返回Map中的最後一個鍵。 |
SortedMap subMap(fromKey,toKey) | 生成此Map的子集,範圍從fromKey(包含)到toKey(不包含)。 |
SortedMap headMap(toKey) | 生成此Map的子集,由鍵小於toKey的所有鍵值對組成。 |
SortedMap tailMap(fromKey) | 生成此Map的子集,由鍵大於或等於fromKey的所有鍵值對組成。 |
(3)LinkedHashMap
LinkedHashMap雜湊化所有的元素,但是在遍歷鍵值對時,卻又以元素的插入順序返回鍵值對。可以在構造器中設定LinkedHashMap,使之採用基於訪問的最近最少使用(LRU)演算法,於是沒有被訪問過的元素就會出現在佇列最前面。
6、雜湊與雜湊碼
Object的hashCode()方法生成雜湊碼,而它預設是使用物件的地址計算雜湊碼。因此,如果要使用自己的類作為HashMap的鍵,必須同時過載hashCode()和equals()。HashMap使用equals()判斷當前的鍵是否與表中存在的鍵相同。
(1)為速度而雜湊
雜湊的價值在於速度:雜湊使得查詢得以快速進行。通過陣列儲存鍵的資訊。通過鍵物件生成雜湊碼,作為陣列的下標。將鍵本身儲存在一個數組的list中。為了解決陣列容量被固定的問題,不同的鍵可以產生相同的下標,也就是會產生衝突。
查詢一個值的過程首先計算雜湊碼,然後使用雜湊碼查詢陣列。在陣列中取得儲存值的list,然後對list中的值使用equals()方法進行線性的查詢。
(2)覆蓋hashCode()
設計hashCode()時最重要的因素就是:無論何時,對同一個物件呼叫hashCode()都應該產生同樣的值。hashCode()方法不能依賴於物件中易變的資料,也不應該依賴於具有唯一性的物件資訊,尤其是使用this的值,因為這樣做無法生成一個新的鍵,使之與put()中原始的鍵值對中的鍵相同。
7、選擇介面的不同實現
儘管實際上只有四種容器:Map、List、Set和Queue,但是每種介面都有不止一個實現版本。選擇哪一個實現可以基於使用某個特定操作的頻率,以及需要的執行速度來在它們中間進行選擇。
(1)對List的選擇
在List中通常ArrayList作為預設首選,只有你需要額外的功能,或者當程式的效能因為經常從表中間進行插入和刪除而變差的時候,才會選擇LinkedList。
(2)對Set的選擇
HashSet的效能基本上總是比TreeSet好,特別是在新增和查詢元素時。只有當需要一個排好序的Set時,才應該使用TreeSet。對於插入操作,LinkedHashSet比HashSet的代價更高,這是由維護連結串列所帶來的額外開銷造成的。
(3)對Map的選擇
除了IdentityHashMap,所有的Map實現的插入操作都會隨著Map尺寸的變大而明顯變慢。
HashTable的效能大體上與HashMap相當,因為它們使用了相同的底層儲存和查詢機制。
TreeMap通常比HashMap要慢。第一選擇應該是HashMap,只有在你要求Map始終保持有序時,才需要使用TreeMap。
LinkedHashMap在插入時比HashMap慢一點,因為它維護雜湊資料結構的同時還要維護連結串列(以保持插入順序)。正是由於這個列表,使得其迭代速度更快。
8、實用方法
(1)List的排序和查詢
排序通過sort()方法實現(sort(List <T>),sort(List<T>,Comparator<? Super T> c))。通過Collections.binarySearch()方法實現查詢。
List如果使用了Comparator進行排序,那麼binarySearch()必須使用相同的Comparator。
1 List<String> list =
2 new ArrayList<String>(Arrays.asList("C","B","a"));
3 Collections.sort(list,String.CASE_INSENSITIVE_ORDER);
4 int index = Collections.binarySearch(
5 list,"B",String.CASE_INSENSITIVE_ORDER);
(2)設定Collection或Map為不可修改
1 Collection<String> c =
2 Collections.unmodifiableCollection(new ArrayList<String>());
3 List<String> a =
4 Collections.unmodifiableList(new ArrayList<String>());
5 Set<String> s =
6 Collections.unmodifiableSet(new HashSet<String>());
7 Map<String,String> m =
8 Collections.unmodifiableMap(new HashMap<String,String>());
(3)Collection或Map的同步控制
快速報錯機制能夠防止多個程序同時修改同一個容器的內容。它會檢查容器上的任何除了你的程序所進行的操作以外的所有變化,一旦它發現其他程序修改了容器,就會立刻丟擲ConcurrentModificationException異常。
1 Collection<String> c =
2 Collections.synchronizedCollection(new ArrayList<String>());
3 List<String> a =
4 Collections.synchronizedList(new ArrayList<String>());
5 Set<String> s =
6 Collections.synchronizedSet(new HashSet<String>());
7 Map<String,String> m =
8 Collections.synchronizedMap(new HashMap<String,String>());
9、持有引用
如果想繼續持有對某個物件的引用,希望以後還能夠訪問到該物件,但是也希望能夠允許垃圾回收器釋放它,這時就應該使用Reference物件。以Reference物件作為你和普通引用之間的媒介(代理),另外,一定不能有普通引用指向那個物件,這樣就能達到上述的目的。有三個繼承自抽象類Reference的類:SoftReference、WeakReference和PhantomReference。
(1)WeakHashMap