1. 程式人生 > 其它 >Java容器深入研究

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

  WeakHashMap用來儲存WeakReference。它允許垃圾回收器自動清理鍵和值,這是一種節約儲存空間的技術。允許清理元素的觸發條件:不再需要此鍵了。

參考於《Java程式設計思想》,第459~524頁