1. 程式人生 > >Java集合類詳細介紹

Java集合類詳細介紹

Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap

Collection介面
  Collection是最基本的集合介面,一個Collection代表一組Object,即Collection的元素(Elements)。
Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自Collection的“子介面”如List和Set。
  所有實現Collection介面的類都必須提供兩個標準的建構函式:無引數的建構函式用於建立一個空的Collection,有一個 Collection引數的建構函式用於建立一個新的Collection,這個新的Collection與傳入的Collection有相同的元素。後一個建構函式允許使用者複製一個Collection。
  如何遍歷Collection中的每一個元素?不論Collection的實際型別如何,它都支援一個iterator()的方法,該方法返回一個迭代子,使用該迭代子即可逐一訪問Collection中每一個元素。典型的用法如下:
    Iterator it = collection.iterator(); // 獲得一個迭代子
    while(it.hasNext()) {
      Object obj = it.next(); // 得到下一個元素
    }
  由Collection介面派生的兩個介面是List和Set。

讓我們轉到對框架實現的研究,具體的集合類遵循命名約定,並將基本資料結構和框架介面相結合。除了四個歷史集合類外,Java 2 框架還引入了六個集合實現,如下表所示。關於歷史集合類如何轉換、比如說,如何修改Hashtable 並結合到框架中,請參閱歷史集合類

介面 實現 歷史集合類
Set HashSet
TreeSet
List ArrayList Vector
LinkedList Stack
Map HashMap Hashtable
TreeMap Properties

這裡沒有 Collection 介面的實現。歷史集合類,之所以這樣命名是因為從 Java 類庫 1.0 發行版就開始沿用至今了。

如果從歷史集合類轉換到新的框架類,主要差異之一在於所有的操作都和新類不同步。您可以往新類中新增同步的實現,但您不能把它從舊的類中除去。

Collection collection = new ArrayList();(這樣寫的好處在於,以後如果要理性不同的集合,可以省略很多麻煩。因為都是用Collection接口裡的方法,)

 boolean add(E o)
          確保此 collection 包含指定的元素(可選操作)。
 boolean
          將指定 collection 中的所有元素都新增到此 collection 中(可選操作)。
 void clear
()

          移除此 collection 中的所有元素(可選操作)。
 boolean
          如果此 collection 包含指定的元素,則返回 true
 boolean
          如果此 collection 包含指定 collection 中的所有元素,則返回 true
 boolean
          比較此 collection 與指定物件是否相等。
 int
          返回此 collection 的雜湊碼值。
 boolean
          如果此 collection 不包含元素,則返回 true

          返回在此 collection 的元素上進行迭代的迭代器。
 boolean
          從此 collection 中移除指定元素的單個例項,如果存在的話(可選操作)。
 boolean
          移除此 collection 中那些也包含在指定 collection 中的所有元素(可選操作)。
 boolean
          僅保留此 collection 中那些也包含在指定 collection 的元素(可選操作)。
 int size()
          返回此 collection 中的元素數。

          返回包含此 collection 中所有元素的陣列。
<T> T[]

          返回包含此 collection 中所有元素的陣列;返回陣列的執行時型別與指定陣列的執行時型別相同。

*All方法引數的型別都為Collection ,大多數方法都是返回boolean型別值,Collection 介面用於表示任何物件或元素組。想要儘可能以常規方式處理一組元素時,就使用這一介面。(如,可以直接add(100),可以是普通資料型別)。

容器類物件在呼叫remove,contains等方法時需要比較物件是否相等地,這會涉及到物件型別的equals方法和hashcode方法。即,相等的物件應該有相等的hashcode.當然,如果是自定義的型別,需要重寫這兩個方法。

iterator介面

 boolean
          如果仍有元素可以迭代,則返回 true
E next()
          返回迭代的下一個元素。
 void
          從迭代器指向的集合中移除迭代器返回的最後一個元素(可選操作)。
Set Set介面同樣是Collection介面的一個子介面,它表示數學意義上的集合概念。Set中不包含重複的元素,即Set中不存兩個這樣的元素e1e2,使得e1.equals(e2)true。由於Set介面提供的資料結構是數學意義上集合概念的抽象,因此它需要支援物件的新增、刪除,而不需提供隨機訪問。故Set介面與Collection的介面相同,在此對裡面的方法不作介紹。
 boolean add(E o)
          如果 set 中尚未存在指定的元素,則新增此元素(可選操作)。
 boolean
          如果 set 中沒有指定 collection 中的所有元素,則將其新增到此 set 中(可選操作)。
 void clear()
          移除 set 中的所有元素(可選操作)。
 boolean
          如果 set 包含指定的元素,則返回 true
 boolean
          如果此 set 包含指定 collection 的所有元素,則返回 true
 boolean
          比較指定物件與此 set 的相等性。
 int
          返回 set 的雜湊碼值。
 boolean
          如果 set 不包含元素,則返回 true

          返回在此 set 中的元素上進行迭代的迭代器。
 boolean
          如果 set 中存在指定的元素,則將其移除(可選操作)。
 boolean
          移除 set 中那些包含在指定 collection 中的元素(可選操作)。
 boolean
          僅保留 set 中那些包含在指定 collection 中的元素(可選操作)。
 int size()
          返回 set 中的元素數(其容量)。

          返回一個包含 set 中所有元素的陣列。
<T> T[]

          返回一個包含 set 中所有元素的陣列;返回陣列的執行時型別是指定陣列的型別。

按照定義,Set 介面繼承 Collection 介面,而且它不允許集合中存在重複項。所有原始方法都是現成的,沒有引入新方法。具體的Set 實現類依賴新增的物件的 方法來檢查等同性。

HashSet 類和 TreeSet 類

“集合框架”支援 Set 介面兩種普通的實現:HashSet 和TreeSet。在更多情況下,您會使用 HashSet 儲存重複自由的集合。考慮到效率,新增到 HashSet 的物件需要採用恰當分配雜湊碼的方式來實現hashCode() 方法。雖然大多數系統類覆蓋了 Object 中預設的實現,但建立您自己的要新增到 HashSet 的類時,別忘了覆蓋 hashCode()。當您要從集合中以有序的方式抽取元素時,TreeSet 實現會有用處。為了能順利進行,新增到TreeSet 的元素必須是可排序的。 “集合框架”新增對 Comparable 元素的支援,在排序的“可比較的介面”部分中會詳細介紹。我們暫且假定一棵樹知道如何保持java.lang 包裝程式器類元素的有序狀態。一般說來,先把元素新增到 HashSet,再把集合轉換為 TreeSet 來進行有序遍歷會更快。

為優化 HashSet 空間的使用,您可以調優初始容量和負載因子。TreeSet 不包含調優選項,因為樹總是平衡的,保證了插入、刪除、查詢的效能為log(n)

HashSet 和 TreeSet 都實現 Cloneable 介面。

集的使用示例

為演示具體 Set 類的使用,下面的程式建立了一個 HashSet,並往裡添加了一組名字,其中有個名字添加了兩次。接著,程式把集中名字的列表打印出來,演示了重複的名字沒有出現。接著,程式把集作為TreeSet 來處理,並顯示有序的列表。

import java.util.*;

public class SetExample {
  public static void main(String args[]) {
    Set set = new HashSet();
    set.add("Bernadine");
    set.add("Elizabeth");
    set.add("Gene");
    set.add("Elizabeth");
    set.add("Clara");
    System.out.println(set);
    Set sortedSet = new TreeSet(set);
    System.out.println(sortedSet);
  }
}

執行程式產生了以下輸出。請注意重複的條目只出現了一次,列表的第二次輸出已按字母順序排序。

[Gene, Clara, Bernadine, Elizabeth]
[Bernadine, Clara, Elizabeth, Gene]

List 介面繼承了 Collection 介面以定義一個允許重複項的有序集合。該介面不但能夠對列表的一部分進行處理,還添加了面向位置的操作。

有序的 collection(也稱為序列)。此介面的使用者可以對列表中每個元素的插入位置進行精確地控制。使用者可以根據元素的整數索引(在列表中的位置)訪問元素,並搜尋列表中的元素。

與 set 不同,列表通常允許重複的元素。更正式地說,列表通常允許滿足 e1.equals(e2) 的元素對 e1e2,並且如果列表本身允許 null 元素的話,通常它們允許多個 null 元素。難免有人希望通過在使用者嘗試插入重複元素時丟擲執行時異常的方法來禁止重複的列表,但我們希望這種用法越少越好。

List 介面在 iteratoraddremoveequalshashCode 方法的協定上加了一些其他約定,超過了 Collection 介面中指定的約定。為方便起見,這裡也包括了其他繼承方法的宣告。

List 介面提供了 4 種對列表元素進行定位(索引)訪問方法。列表(像 Java 陣列一樣)是基於 0 的。注意,這些操作可能在和某些實現(例如 LinkedList 類)的索引值成比例的時間內執行。因此,如果呼叫方不知道實現,那麼在列表元素上迭代通常優於用索引遍歷列表。

List 介面提供了特殊的迭代器,稱為 ListIterator,除了允許 Iterator 介面提供的正常操作外,該迭代器還允許元素插入和替換,以及雙向訪問。還提供了一個方法來獲取從列表中指定位置開始的列表迭代器。

List 介面提供了兩種搜尋指定物件的方法。從效能的觀點來看,應該小心使用這些方法。在很多實現中,它們將執行高開銷的線性搜尋。

List 介面提供了兩種在列表的任意位置高效插入和移除多個元素的方法。

注意:儘管列表允許把自身作為元素包含在內,但建議要特別小心:在這樣的列表上,equalshashCode 方法不再是定義良好的。

某些列表實現對列表可能包含的元素有限制。例如,某些實現禁止 null 元素,而某些實現則對元素的型別有限制。試圖新增不合格的元素會丟擲未經檢查的異常,通常是 NullPointerExceptionClassCastException。試圖查詢不合格的元素是否存在可能會丟擲異常,也可能簡單地返回 false;某些實現會採用前一種行為,而某些則採用後者。概括地說,試圖對不合格元素執行操作時,如果完成該操作後不會導致在列表中插入不合格的元素,則該操作可能丟擲一個異常,也可能成功,這取決於實現的選擇。此介面的規範中將這樣的異常標記為“可選”。

面向位置的操作包括插入某個元素或 Collection 的功能,還包括獲取、除去或更改元素的功能。在 List 中搜索元素可以從列表的頭部或尾部開始,如果找到元素,還將報告元素所在的位置。

  • void add(int index, Object element)
  • boolean addAll(int index, Collection collection)
  • Object get(int index)
  • int indexOf(Object element)
  • int lastIndexOf(Object element)
  • Object remove(int index)
  • Object set(int index, Object element)

List 介面不但以位置友好的方式遍歷整個列表,還能處理集合的子集:

  • ListIterator listIterator()
  • ListIterator listIterator(int startIndex)
  • List subList(int fromIndex, int toIndex)

處理 時,位於 fromIndex 的元素在子列表中,而位於 toIndex 的元素則不是,提醒這一點很重要。以下 for-loop 測試案例大致反映了這一點:

for (int i=fromIndex; i<toIndex; i++) {
  // process element at position i
}

此外,我們還應該提醒的是 ― 對子列表的更改(如 add()remove()set() 呼叫)對底層 List 也有影響。

 booleanadd(E o)
          向列表的尾部追加指定的元素(可選操作)。
 voidadd(int index, E element)
          在列表的指定位置插入指定元素(可選操作)。
 boolean
          追加指定 collection 中的所有元素到此列表的結尾,順序是指定 collection 的迭代器返回這些元素的順序(可選操作)。
 boolean
          將指定 collection 中的所有元素都插入到列表中的指定位置(可選操作)。
 voidclear()
          從列表中移除所有元素(可選操作)。
 boolean
          如果列表包含指定的元素,則返回 true
 boolean
          如果列表包含指定 collection 的所有元素,則返回 true
 boolean
          比較指定的物件與列表是否相等。
Eget(int index)
          返回列表中指定位置的元素。
 int
          返回列表的雜湊碼值。
 int
          返回列表中首次出現指定元素的索引,如果列表不包含此元素,則返回 -1。
 boolean
          如果列表不包含元素,則返回 true

          返回以正確順序在列表的元素上進行迭代的迭代器。
 int
          返回列表中最後出現指定元素的索引,如果列表不包含此元素,則返回 -1。

          返回列表中元素的列表迭代器(以正確的順序),從列表的指定位置開始。
E(int index)
          移除列表中指定位置的元素(可選操作)。
 boolean
          移除列表中出現的首個指定元素(可選操作)。
 boolean
          從列表中移除指定 collection 中包含的所有元素(可選操作)。
 boolean
          僅在列表中保留指定 collection 中所包含的元素(可選操作)。
Eset(int index, E element)
          用指定元素替換列表中指定位置的元素(可選操作)。
 intsize()
          返回列表中的元素數。
List<E>(int fromIndex, int toIndex)
          返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之間的部分檢視。

          返回以正確順序包含列表中的所有元素的陣列。
<T> T[]

          返回以正確順序包含列表中所有元素的陣列;返回陣列的執行時型別是指定陣列的執行時型別。

其中set方法返回的是被替換的內容。

Linked 改快讀慢

Array 讀快改慢

Hash 兩都之間

Collection是集合介面
    |————Set子介面:無序,不允許重複。
    |————List子介面:有序,可以有重複元素。

    區別:Collections是集合類

    Set和List對比:
    Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變。
    List:和陣列類似,List可以動態增長,查詢元素效率高,插入刪除元素效率低,因為會引起其他元素位置改變。

    Set和List具體子類:
    Set
     |————HashSet:以雜湊表的形式存放元素,插入刪除速度很快。

    List
     |————ArrayList:動態陣列
     |————LinkedList:連結串列、佇列、堆疊。

    Array和java.util.Vector
    Vector是一種老的動態陣列,是執行緒同步的,效率很低,一般不贊成使用。

Map 介面

Map 介面不是 Collection 介面的繼承。而是從自己的用於維護鍵-值關聯的介面層次結構入手。按定義,該介面描述了從不重複的鍵到值的對映。

我們可以把這個介面方法分成三組操作:改變、查詢和提供可選檢視。

改變操作允許您從對映中新增和除去鍵-值對。鍵和值都可以為 null。但是,您不能把Map 作為一個鍵或值新增給自身。

  • Object put(Object key, Object value)返回值是被替換的值。
  • Object remove(Object key)
  • void putAll(Map mapping)
  • void clear()

查詢操作允許您檢查對映內容:

  • Object get(Object key)
  • boolean containsKey(Object key)
  • boolean containsValue(Object value)
  • int size()
  • boolean isEmpty()

最後一組方法允許您把鍵或值的組作為集合來處理。

  • public Set keySet()
  • public Collection values()
  • public Set entrySet()

因為對映中鍵的集合必須是唯一的,您用 Set 支援。因為對映中值的集合可能不唯一,您用Collection 支援。最後一個方法返回一個實現 Map.Entry 介面的元素 Set

Map.Entry 介面

Map 的 entrySet() 方法返回一個實現Map.Entry 介面的物件集合。集合中每個物件都是底層 Map 中一個特定的鍵-值對。

通過這個集合迭代,您可以獲得每一條目的鍵或值並對值進行更改。但是,如果底層 Map 在Map.Entry 介面的setValue() 方法外部被修改,此條目集就會變得無效,並導致迭代器行為未定義。

HashMap 類和 TreeMap 類

“集合框架”提供兩種常規的 Map 實現:HashMap 和TreeMap。和所有的具體實現一樣,使用哪種實現取決於您的特定需要。在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。但如果您要按順序遍歷鍵,那麼TreeMap會更好。根據集合大小,先把元素新增到 HashMap,再把這種對映轉換成一個用於有序鍵遍歷的 TreeMap 可能更快。使用HashMap 要求新增的鍵類明確定義了 hashCode() 實現。有了TreeMap 實現,新增到對映的元素一定是可排序的。我們將在排序中詳細介紹。

為了優化 HashMap 空間的使用,您可以調優初始容量和負載因子。這個TreeMap 沒有調優選項,因為該樹總處於平衡狀態。

HashMap 和 TreeMap 都實現Cloneable 介面。

對映的使用示例

以下程式演示了具體 Map 類的使用。該程式對自命令列傳遞的詞進行頻率計數。HashMap 起初用於資料儲存。後來,對映被轉換為TreeMap 以顯示有序的鍵列列表。

import java.util.*;

public class MapExample {
  public static void main(String args[]) {
    Map map = new HashMap();
    Integer ONE = new Integer(1);
    for (int i=0, n=args.length; i<n; i++) {
      String key = args[i];
      Integer frequency = (Integer)map.get(key);
      if (frequency == null) {
        frequency = ONE;
      } else {
        int value = frequency.intValue();
        frequency = new Integer(value + 1);
      }
      map.put(key, frequency);
    }
    System.out.println(map);
    Map sortedMap = new TreeMap(map);
    System.out.println(sortedMap);
  }
}

用 Bill of Rights 的第三篇文章的文字執行程式產生下列輸出,請注意有序輸出看起來多麼有用!

無序輸出:

{prescribed=1, a=1, time=2, any=1, no=1, shall=1, nor=1, peace=1, owner=1, soldier=1, to=1, the=2, law=1, but=1, manner=1, without=1, house=1, in=4, by=1, consent=1, war=1, quartered=1, be=2, of=3}

有序輸出:

{a=1, any=1, be=2, but=1, by=1, consent=1, house=1, in=4, law=1, manner=1, no=1, nor=1, of=3, owner=1, peace=1, prescribed=1, quartered=1, shall=1, soldier=1, the=2, time=2, to=1, war=1, without=1}

 Java集合框架是最常被問到的Java面試問題,要理解Java技術強大特性就有必要掌握集合框架。這裡有一些實用問題,常在核心Java面試中問到。

  1、什麼是Java集合API

  Java集合框架API是用來表示和操作集合的統一框架,它包含介面、實現類、以及幫助程式設計師完成一些程式設計的演算法。簡言之,API在上層完成以下幾件事:

  ● 程式設計更加省力,提高城程式速度和程式碼質量

  ● 非關聯的API提高互操作性

  ● 節省學習使用新API成本

  ● 節省設計新API的時間

  ● 鼓勵、促進軟體重用

  具體來說,有6個集合介面,最基本的是Collection介面,由三個介面Set、List、SortedSet繼承,另外兩個介面是Map、SortedMap,這兩個介面不繼承Collection,表示對映而不是真正的集合。

  2、什麼是Iterator

  一些集合類提供了內容遍歷的功能,通過java.util.Iterator介面。這些介面允許遍歷物件的集合。依次操作每個元素物件。當使用 Iterators時,在獲得Iterator的時候包含一個集合快照。通常在遍歷一個Iterator的時候不建議修改集合本省。

  3、Iterator與ListIterator有什麼區別?

  Iterator:只能正向遍歷集合,適用於獲取移除元素。ListIerator:繼承Iterator,可以雙向列表的遍歷,同樣支援元素的修改。

  4、什麼是HaspMap和Map?

  Map是介面,Java 集合框架中一部分,用於儲存鍵值對,HashMap是用雜湊演算法實現Map的類。

  5、HashMap與HashTable有什麼區別?對比Hashtable VS HashMap

  兩者都是用key-value方式獲取資料。Hashtable是原始集合類之一(也稱作遺留類)。HashMap作為新集合框架的一部分在Java2的1.2版本中加入。它們之間有一下區別:

  ● HashMap和Hashtable大致是等同的,除了非同步和空值(HashMap允許null值作為key和value,而Hashtable不可以)。

  ● HashMap沒法保證對映的順序一直不變,但是作為HashMap的子類LinkedHashMap,如果想要預知的順序迭代(預設按照插入順序),你可以很輕易的置換為HashMap,如果使用Hashtable就沒那麼容易了。

  ● HashMap不是同步的,而Hashtable是同步的。

  ● 迭代HashMap採用快速失敗機制,而Hashtable不是,所以這是設計的考慮點。

  6、在Hashtable上下文中同步是什麼意思?

  同步意味著在一個時間點只能有一個執行緒可以修改雜湊表,任何執行緒在執行hashtable的更新操作前需要獲取物件鎖,其他執行緒等待鎖的釋放。

  7、什麼叫做快速失敗特性

  從高級別層次來說快速失敗是一個系統或軟體對於其故障做出的響應。一個快速失敗系統設計用來即時報告可能會導致失敗的任何故障情況,它通常用來停止正常的操作而不是嘗試繼續做可能有缺陷的工作。當有問題發生時,快速失敗系統即時可見地發錯錯誤告警。在Java中,快速失敗與iterators有關。如果一個iterator在集合物件上建立了,其它執行緒欲“結構化”的修改該集合物件,併發修改異常 (ConcurrentModificationException) 丟擲。

  8、怎樣使Hashmap同步?

  HashMap可以通過Map m = Collections.synchronizedMap(hashMap)來達到同步的效果。

  9、什麼時候使用Hashtable,什麼時候使用HashMap

  基本的不同點是Hashtable同步HashMap不是的,所以無論什麼時候有多個執行緒訪問相同例項的可能時,就應該使用Hashtable,反之使用HashMap。非執行緒安全的資料結構能帶來更好的效能。

  如果在將來有一種可能—你需要按順序獲得鍵值對的方案時,HashMap是一個很好的選擇,因為有HashMap的一個子類 LinkedHashMap。所以如果你想可預測的按順序迭代(預設按插入的順序),你可以很方便用LinkedHashMap替換HashMap。反觀要是使用的Hashtable就沒那麼簡單了。同時如果有多個執行緒訪問HashMap,Collections.synchronizedMap()可以代替,總的來說HashMap更靈活。

  10、為什麼Vector類認為是廢棄的或者是非官方地不推薦使用?或者說為什麼我們應該一直使用ArrayList而不是Vector

  你應該使用ArrayList而不是Vector是因為預設情況下你是非同步訪問的,Vector同步了每個方法,你幾乎從不要那樣做,通常有想要同步的是整個操作序列。同步單個的操作也不安全(如果你迭代一個Vector,你還是要加鎖,以避免其它執行緒在同一時刻改變集合).而且效率更慢。當然同樣有鎖的開銷即使你不需要,這是個很糟糕的方法在預設情況下同步訪問。你可以一直使用Collections.sychronizedList來裝飾一個集合。

  事實上Vector結合了“可變陣列”的集合和同步每個操作的實現。這是另外一個設計上的缺陷。Vector還有些遺留的方法在列舉和元素獲取的方法,這些方法不同於List介面,如果這些方法在程式碼中程式設計師更趨向於想用它。儘管列舉速度更快,但是他們不能檢查如果集合在迭代的時候修改了,這樣將導致問題。儘管以上諸多原因,Oracle也從沒宣稱過要廢棄Vector。