Java 集合系列之三:Set基本操作


1. Java Set

1. Java Set 重要觀點

  • Java Set接口是Java Collections Framework的成員。
  • Set不允許出現重復元素-----------無重復
  • Set不保證集合中元素的順序---------無序
  • Set允許包含值為null的元素,但最多只能有一個null元素。
  • Set支持泛型(類型的參數化),我們應盡可能使用它。將Generics與List一起使用將在運行時避免ClassCastException。
  • 先去看Map,Set的實現類都是基於Map來實現的(如,HashSet是通過HashMap實現的,TreeSet是通過TreeMap實現的,LinkedHashSet是通過LinkedHashMap來實現的)。 

2. Java Set類圖

Java Set接口擴展了Collection接口。Collection接口 externs Iterable接口。

一些最常用的Set實現類是HashSet,LinkedHashSet,TreeSet,SortedSet,CopyOnWriteArraySet。

AbstractSet提供了Set接口的骨干實現,以減少實現List的工作量。

 

3. Java Set 方法

 boolean    add(E e) //如果 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 中所有元素的數組;返回數組的運行時類型是指定數組的類型。

2. HashSet

1. HashSet 結構圖

HashSet,由哈希表(實際上是一個 HashMap 實例)支持。它不保證 set 的迭代順序;特別是它不保證該順序恆久不變。此類允許使用 null元素。!
HashSet繼承了AbstractSet,實現了Cloneable和Serializable接口!

  • 實現了Cloneable接口,即覆蓋了函數clone(),實現淺拷貝。
  • 實現了Serializable接口,支持序列化,能夠通過序列化傳輸。

2. HashSet 重要特點

  1. 依賴於哈希表(實際上是一個 HashMap 實例)(哈希表+鏈表+紅黑樹),不可以存儲相同元素(排重)
  2. 底層實現是一個HashMap(保存數據),實現Set接口。(HashSet中含有一個”HashMap類型的成員變量”map,HashSet的操作函數,實際上都是通過map實現的。)
  3. 非同步,線程不安全,存取速度快(同步封裝Set s = Collections.synchronizedSet(new HashSet(...));
  4. 默認初始容量為16。
  5. 加載因子為0.75:即當 元素個數 超過 容量長度的0.75倍 時,進行擴容
  6. 擴容增量:原容量的 1 倍,如 HashSet的容量為16,一次擴容后是容量為32
  7. 重寫hashCode():HashSet集合排重時,需要判斷兩個對象是否相同,對象相同的判斷可以通過hashCode值判斷,所以需要重寫hashCode()方法
  8. 重寫equals():equals()方法是Object類中的方法,表示比較兩個對象是否相等,若不重寫相當於比較對象的地址, 所以我們可以嘗試重寫equals方法,檢查是否排重。
  9. 會根據hashcode和equals來龐端是否是同一個對象,如果hashcode一樣,並且equals返回true,則是同一個對象,不能重復存放。 
  10. fail-fast機制:HashSet通過iterator()返回的迭代器是fail-fast的。
  11. 兩種遍歷方法:Iterator【iterator.next()】,forEach【set.toArray();】
Set<String> set = new HashSet<String>();
set.add("first");
set.add("second");
set.add("three");

// foreach
for (String string : set) {
    System.out.println(string);
}

// iterator
Iterator<String> setIterator = set.iterator();
while (setIterator.hasNext()) {
    String string = (String) setIterator.next();
    System.out.println(string);
}

3. TreeSet

1. TreeSet 結構圖

基於 TreeMap 的 NavigableSet 實現。使用元素的自然順序對元素進行排序,或者根據創建 set 時提供的 Comparator進行排序,具體取決於使用的構造方法。

  TreeSet也不能存放重復對象,但是TreeSet會自動排序,如果存放的對象不能排序則會報錯,所以存放的對象必須指定排序規則。排序規則包括自然排序和客戶排序。

  ①自然排序:TreeSet要添加哪個對象就在哪個對象類上面實現java.lang.Comparable接口,並且重寫comparaTo()方法,返回0則表示是同一個對象,否則為不同對象。

  ②客戶排序:建立一個第三方類並實現java.util.Comparator接口。並重寫方法。定義集合形式為TreeSet ts = new TreeSet(new 第三方類());


TreeSet繼承了AbstractSet,實現了NavigableSet、Cloneable和Serializable接口!

  • 繼承於AbstractSet,AbstractSet實現了equals和hashcode方法。
  • 實現了NavigableSet接口,意味着它支持一系列的導航方法。比如查找與指定目標最匹配項。
  • 實現了Cloneable接口,即覆蓋了函數clone(),實現淺拷貝。
  • 實現了Serializable接口,支持序列化,能夠通過序列化傳輸。
  •  TreeSet是SortedSet接口的實現類

2. TreeSet 重要特點

  1. 依賴於TreeMap,TreeSet是基於TreeMap實現的。(紅黑樹)復雜度為O(log (n))
  2. 不可以存儲相同元素(排重),自動排序。(有序集合)
  3. TreeSet中不允許使用null元素!在添加的時候如果添加null,則會拋出NullPointerException異常。
  4. TreeSet是非同步的方法【SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));】。
  5. 它的iterator 方法返回的迭代器是fail-fast的。
  6. TreeSet不支持快速隨機遍歷,只能通過迭代器進行遍歷! 兩種遍歷方法:Iterator【iterator.next()】,forEach【set.toArray();】
  7. TreeSet中的元素支持2種排序方式:自然排序 或者 根據創建TreeSet 時提供的 Comparator 進行排序。這取決於使用的構造方法。
  8. 自然排序,重寫compareTo方法:元素所屬的類需要實現java.lang.Comparable接口,並重寫compareTo方法。 compareTo方法除了可以進行排序外,還有排重的功能,但是必須在compareTo方法中對類中所有的屬性值都進行判斷,否則不比較那個屬性,排重就會忽略哪個屬性
  9. 定制排序,重寫compare方法:元素需要通過java.util.Comparator接口(比較器)中的compare方法進行比較大小,並排序。 compare方法除了可以進行排序外,還有排重的功能,但是必須在compare方法中對類中所有的屬性值都進行判斷,否則不比較那個屬性,排重就會忽略哪個屬性

  ps:Comparable中的compareTo()一個參數, Comparator中compare()兩個參數,返回值都是int類型,如果返回0,表示兩個比較元素相同,如果大於0 ,前面大於后面,如果小於0,前面小於后面。

3. HashSet vs TreeSet

  1. HashSet是一個無序的集合,基於HashMap實現;TreeSet是一個有序的集合,基於TreeMap實現。
  2. HashSet集合中允許有null元素,TreeSet集合中不允許有null元素。
  3. HashSet和TreeSet都是非同步!在使用Iterator進行迭代的時候要注意fail-fast。

4. LinkedHashSet

1. LinkedHashSet 結構圖

LinkedHashSet類:LinkedHashSet正好介於HashSet和TreeSet之間,它也是一個hash表,但它同時維護了一個雙鏈表來記錄插入的順序,基本方法的復雜度為O(1)。

當遍歷該集合時候,LinkedHashSet將會以元素的添加順序訪問集合的元素。

2. LinkedHashSet 重要特點

  1. 繼承自HashSet,與HashSet唯一的區別是LinkedHashSet內部使用的是LinkHashMap((哈希表+鏈表+紅黑樹)+雙向鏈表)。
  2.  LinkedHashSet在迭代訪問Set中的全部元素時,性能比HashSet好,但是插入時性能稍微遜色於HashSet。
  3. 非同步,線程不安全,存取速度快(同步封裝 Set s = Collections.synchronizedSet(new LinkedHashSet(...));
  4. 其他同HashSet
  5. 維護插入順序,LinkedHashSet使用LinkedHashMap對象來存儲它的元素,插入到LinkedHashSet中的元素實際上是被當作LinkedHashMap的鍵保存起來的
  6. LinkedHashMap的每一個鍵值對都是通過內部的靜態類Entry<K, V>實例化的。這個 Entry<K, V>類繼承了HashMap.Entry類。這個靜態類增加了兩個成員變量,before和after來維護LinkedHasMap元素的插入順序。這兩個成員變量分別指向前一個和后一個元素,這讓LinkedHashMap也有類似雙向鏈表的表現。

 

4. ConcurrentSkipListSet

1. ConcurrentSkipListSet 結構圖

2. ConcurrentSkipListSet 重要特點

  1. 一個基於 ConcurrentSkipListMap 的可縮放並發 NavigableSet 實現。set 的元素可以根據它們的自然順序進行排序,也可以根據創建 set 時所提供的 Comparator 進行排序,具體取決於使用的構造方法。
  2. 此實現為 containsaddremove 操作及其變體提供預期平均 log(n) 時間開銷。多個線程可以安全地並發執行插入、移除和訪問操作。迭代器是弱一致 的,返回的元素將反映迭代器創建時或創建后某一時刻的 set 狀態。它們 拋出 ConcurrentModificationException,可以並發處理其他操作。升序排序視圖及其迭代器比降序排序視圖及其迭代器更快。
  3. 請注意,與在大多數 collection 中不同,這里的 size 方法不是 一個固定時間 (constant-time) 操作。由於這些 set 的異步特性,確定元素的當前數目需要遍歷元素。此外,批量操作 addAllremoveAllretainAll 和 containsAll 並不 保證能以原子方式 (atomically) 執行。例如,與 addAll 操作一起並發操作的迭代器只能查看某些附加元素。
  4. 此類不允許使用 null 元素,因為無法可靠地將 null 參數及返回值與不存在的元素區分開來。

4. CopyOnWriteArraySet (JUC)

1. CopyOnWriteArraySet 結構圖

2. CopyOnWriteArraySet 重要特點

  1. CopyOnWriteArraySet 是線程安全的 Set,它是 由 CopyOnWriteArrayList 實現內部持有一個 CopyOnWriteArrayList 引用,所有的操作都是由 CopyOnWriteArrayList 來實現的,區別就是 CopyOnWriteArraySet 是無序的,並且不允許存放重復值。由於是一個Set,所以也不支持隨機索引元素。
  2. 適合元素比較少,並且讀取操作高於更新(add/set/remove)操作的場景
  3. 由於每次更新需要復制內部數組,所以更新操作(addset 和 remove 等等)開銷比較大。
  4. 內部的迭代器 iterator 使用了不變的“快照”技術,存儲了內部數組快照, 所以它的 iterator 不支持可變remove、set、add操作,但是通過迭代器進行並發讀取時效率很高。
  5. 它是線程安全的。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM