1. 程式人生 > >Map、Set、List集合差別及聯系詳解

Map、Set、List集合差別及聯系詳解

特性 互轉 字母順序 時也 參數 很慢 未定義 諸多 cto

提到集合之前,先說說數組Array和集合的區別:

  (1)數組是大小固定的,並且同一個數組只能存放類型一樣的數據(基本類型/引用類型)   (2)JAVA集合可以存儲和操作數目不固定的一組數據。    (3)若程序時不知道究竟需要多少對象,需要在空間不足時自動擴增容量,則需要使用容器類庫,array不適用。     FYI:使用相應的toArray()和Arrays.asList()方法可以相互轉換。

一、集合

  集合類存放於java.util包中。
  集合類存放的都是對象的引用,而非對象本身,出於表達上的便利,我們稱集合中的對象就是指集合中對象的引用(reference)。
  集合類型主要有3種:set(集)、list(列表)和map(映射)。

一、這三者什麽關系呢

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

技術分享

1.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(); // 得到下一個元素
} 

  iterator接口:

技術分享

  由Collection接口派生的兩個接口是List和Set。

2.Set

  Set接口同樣是Collection接口的一個子接口,它表示數學意義上的集合概念。Set中不包含重復的元素,即Set中不存兩個這樣的元素e1e2,使得e1.equals(e2)true。由於Set接口提供的數據結構是數學意義上集合概念的抽象,因此它需要支持對象的添加、刪除,而不需提供隨機訪問。故Set接口與Collection的接口相同。

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

  HashSet: 使用HashMap的一個集的實現。雖然集定義成無序,但必須存在某種方法能相當高效地找到一個對象。使用一個HashMap對象實現集的存儲和檢索操作是在固定時間內實現的.

  TreeSet: 在集中以升序對對象排序的集的實現。這意味著從一個TreeSet對象獲得第一個叠代器將按升序提供對象。TreeSet類使用了一個TreeMap.

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

  HashSetTreeSet 都實現 Cloneable 接口。

  當您要從集合中以有序的方式抽取元素時,TreeSet實現會有用處。為了能順利進行,添加到TreeSet的元素必須是可排序的。    Set的演示:

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]

3.List

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

實際上有兩種List: 一種是基本的ArrayList,其優點在於隨機訪問元素,另一種是更強大的LinkedList,它並不是為快速隨機訪問設計的,而是具有一套更通用的方法。

  List : 次序是List最重要的特點:它保證維護元素特定的順序。List為Collection添加了許多方法,使得能夠向List中間插入與移除元素(這只推薦LinkedList使用。)一個List可以生成ListIterator,使用它可以從兩個方向遍歷List,也可以從List中間插入和移除元素。    ArrayList : 由數組實現的List。允許對元素進行快速隨機訪問,但是向List中間插入與移除元素的速度很慢。ListIterator只應該用來由後向前遍歷ArrayList,而不是用來插入和移除元素。因為那比LinkedList開銷要大很多。

  LinkedList : 對順序訪問進行了優化,向List中間插入與刪除的開銷並不大,隨機訪問則相對較慢。(使用ArrayList代替。)還具有下列方法:addFirst(), addLast(), getFirst(), getLast(), removeFirst() 和 removeLast(), 這些方法 (沒有在任何接口或基類中定義過)使得LinkedList可以當作堆棧、隊列和雙向隊列使用

  Vector:實現一個類似數組一樣的表,自動增加容量來容納你所需的元素。使用下標存儲和檢索對象就象在一個標準的數組中一樣。你也可以用一個叠代器從一個Vector中檢索對象。Vector是唯一的同步容器類!!當兩個或多個線程同時訪問時也是性能良好的。

  Stsck: 這個類從Vector派生而來,並且增加了方法實現棧!一種後進先出的存儲結構。

  面向位置的操作包括插入某個元素或 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
}

List用法示例:

技術分享
其中set方法返回的是被替換的內容。

技術分享

4.List和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是一種老的動態數組,是線程同步的,效率很低,一般不贊成使用。

5.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 接口

  MapentrySet()方法返回一個實現Map.Entry接口的對象集合。集合中每個對象都是底層Map中一個特定的鍵-值對。

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

HashMap 類和 TreeMap 類

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

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

  HashMapTreeMap都實現Cloneable接口。

  Hashtable類和Properties類是Map接口的歷史實現。

  HashTable: 實現一個映象,所有的鍵必須非空。為了能高效的工作,定義鍵的類必須實現hashcode()方法和equal()方法。這個類是前面java實現的一個繼承,並且通常能在實現映象的其他類中更好的使用。

  HashMap: 實現一個映象,允許存儲空對象,而且允許鍵是空(由於鍵必須是唯一的,當然只能有一個)。

  WeakHashMap: 實現這樣一個映象:通常如果一個鍵對一個對象而言不再被引用,鍵/對象對將被舍棄。這與HashMap形成對照,映象中的鍵維持鍵/對象對的生命周期,盡管使用映象的程序不再有對鍵的引用,並且因此不能檢索對象。

  TreeMap實現這樣一個映象,對象是按鍵升序排列的。

映射的使用示例:

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

結果:

//無序輸出:
{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}

解疑:

  1、什麽是Iterator

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

  2、Iterator與ListIterator有什麽區別?

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

  3、什麽是HaspMap和Map?

  Map是接口,Java 集合框架中一部分,用於存儲鍵值對,HashMap是用哈希算法實現Map的類。

  4、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不是,所以這是設計的考慮點。

  5、在Hashtable上下文中同步是什麽意思?

  同步意味著在一個時間點只能有一個線程可以修改哈希表,任何線程在執行hashtable的更新操作前需要獲取對象鎖,其他線程等待鎖的釋放。

  6、什麽叫做快速失敗特性

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

  7、怎樣使Hashmap同步?

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

  8、什麽時候使用Hashtable,什麽時候使用HashMap

  基本的不同點是Hashtable同步HashMap不是的,所以無論什麽時候有多個線程訪問相同實例的可能時,就應該使用Hashtable,反之使用HashMap。非線程安全的數據結構能帶來更好的性能。

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

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

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

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

Map、Set、List集合差別及聯系詳解