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 addAll(Collection<? extends E> c)
          將指定 collection 中的所有元素都添加到此 collection 中(可選操作)。
 void clear()
          移除此 collection 中的所有元素(可選操作)。
 boolean contains(Object o)
          如果此 collection 包含指定的元素,則返回 true
 boolean containsAll(Collection<?> c)
          如果此 collection 包含指定 collection 中的所有元素,則返回true
 boolean equals(Object o)
          比較此 collection 與指定對象是否相等。
 int hashCode()
          返回此 collection 的哈希碼值。
 boolean isEmpty()
          如果此 collection 不包含元素,則返回 true
 Iterator<E> iterator()
          返回在此 collection 的元素上進行迭代的迭代器。
 boolean remove(Object o)
          從此 collection 中移除指定元素的單個實例,如果存在的話(可選操作)。
 boolean removeAll(Collection<?> c)
          移除此 collection 中那些也包含在指定 collection 中的所有元素(可選操作)。
 boolean retainAll(Collection<?> c)
          僅保留此 collection 中那些也包含在指定 collection 的元素(可選操作)。
 int size()
          返回此 collection 中的元素數。
 Object[] toArray()
          返回包含此 collection 中所有元素的數組。
<T> T[]
toArray(T[] a)
          返回包含此 collection 中所有元素的數組;返回數組的運行時類型與指定數組的運行時類型相同。

*All方法參數的類型都爲Collection ,大多數方法都是返回boolean類型值,Collection 接口用於表示任何對象或元素組。想要儘可能以常規方式處理一組元素時,就使用這一接口。(如,可以直接add(100),可以是普通數據類型)。

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

iterator接口

 boolean hasNext()
          如果仍有元素可以迭代,則返回 true
 E next()
          返回迭代的下一個元素。
 void remove()
          從迭代器指向的集合中移除迭代器返回的最後一個元素(可選操作)。
Set
Set接口同樣是Collection接口的一個子接口,它表示數學意義上的集合概念。Set中不包含重複的元素,即Set中不存兩個這樣的元素e1e2,使得e1.equals(e2)true。由於Set接口提供的數據結構是數學意義上集合概念的抽象,因此它需要支持對象的添加、刪除,而不需提供隨機訪問。故Set接口與Collection的接口相同,在此對裏面的方法不作介紹。
 boolean add(E o)
          如果 set 中尚未存在指定的元素,則添加此元素(可選操作)。
 boolean addAll(Collection<? extends E> c)
          如果 set 中沒有指定 collection 中的所有元素,則將其添加到此 set 中(可選操作)。
 void clear()
          移除 set 中的所有元素(可選操作)。
 boolean contains(Object o)
          如果 set 包含指定的元素,則返回 true
 boolean containsAll(Collection<?> c)
          如果此 set 包含指定 collection 的所有元素,則返回 true
 boolean equals(Object o)
          比較指定對象與此 set 的相等性。
 int hashCode()
          返回 set 的哈希碼值。
 boolean isEmpty()
          如果 set 不包含元素,則返回 true
 Iterator<E> iterator()
          返回在此 set 中的元素上進行迭代的迭代器。
 boolean remove(Object o)
          如果 set 中存在指定的元素,則將其移除(可選操作)。
 boolean removeAll(Collection<?> c)
          移除 set 中那些包含在指定 collection 中的元素(可選操作)。
 boolean retainAll(Collection<?> c)
          僅保留 set 中那些包含在指定 collection 中的元素(可選操作)。
 int size()
          返回 set 中的元素數(其容量)。
 Object[] toArray()
          返回一個包含 set 中所有元素的數組。
<T> T[]
toArray(T[] a)
          返回一個包含 set 中所有元素的數組;返回數組的運行時類型是指定數組的類型。

按照定義,Set 接口繼承 Collection 接口,而且它不允許集合中存在重複項。所有原始方法都是現成的,沒有引入新方法。具體的Set 實現類依賴添加的對象的 equals()方法來檢查等同性。

HashSet 類和 TreeSet 類

“集合框架”支持 Set 接口兩種普通的實現:HashSet 和TreeSet。在更多情況下,您會使用 HashSet 存儲重複自由的集合。考慮到效率,添加到 HashSet 的對象需要採用恰當分配散列碼的方式來實現hashCode() 方法。雖然大多數系統類覆蓋了 Object 中缺省的hashCode()實現,但創建您自己的要添加到 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 接口

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)

處理 subList() 時,位於 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)
          在列表的指定位置插入指定元素(可選操作)。
 booleanaddAll(Collection<? extends E> c)
          追加指定 collection 中的所有元素到此列表的結尾,順序是指定 collection 的迭代器返回這些元素的順序(可選操作)。
 booleanaddAll(int index, Collection<? extends E> c)
          將指定 collection 中的所有元素都插入到列表中的指定位置(可選操作)。
 voidclear()
          從列表中移除所有元素(可選操作)。
 booleancontains(Object o)
          如果列表包含指定的元素,則返回 true
 booleancontainsAll(Collection<?> c)
          如果列表包含指定 collection 的所有元素,則返回 true
 booleanequals(Object o)
          比較指定的對象與列表是否相等。
 Eget(int index)
          返回列表中指定位置的元素。
 inthashCode()
          返回列表的哈希碼值。
 intindexOf(Object o)
          返回列表中首次出現指定元素的索引,如果列表不包含此元素,則返回 -1。
 booleanisEmpty()
          如果列表不包含元素,則返回 true
 Iterator<E>iterator()
          返回以正確順序在列表的元素上進行迭代的迭代器。
 intlastIndexOf(Object o)
          返回列表中最後出現指定元素的索引,如果列表不包含此元素,則返回 -1。
 ListIterator<E>listIterator()
          返回列表中元素的列表迭代器(以正確的順序)。
 ListIterator<E>listIterator(int index)
          返回列表中元素的列表迭代器(以正確的順序),從列表的指定位置開始。
 Eremove(int index)
          移除列表中指定位置的元素(可選操作)。
 booleanremove(Object o)
          移除列表中出現的首個指定元素(可選操作)。
 booleanremoveAll(Collection<?> c)
          從列表中移除指定 collection 中包含的所有元素(可選操作)。
 booleanretainAll(Collection<?> c)
          僅在列表中保留指定 collection 中所包含的元素(可選操作)。
 Eset(int index, E element)
          用指定元素替換列表中指定位置的元素(可選操作)。
 intsize()
          返回列表中的元素數。
 List<E>subList(int fromIndex, int toIndex)
          返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之間的部分視圖。
 Object[]toArray()
          返回以正確順序包含列表中的所有元素的數組。
<T> T[]
toArray(T[] a)
          返回以正確順序包含列表中所有元素的數組;返回數組的運行時類型是指定數組的運行時類型。

其中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 接口。

Hashtable 類和 Properties 類是Map 接口的歷史實現。我們將在Dictionary 類、Hashtable 類和 Properties 類中討論。

映射的使用示例

以下程序演示了具體 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。


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