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

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

提到集合之前,先說說陣列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)。JavaSDK不提供直接繼承自Collection的類,JavaSDK提供的類都是繼承自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中不存兩個這樣的元素e1和e2,使得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。