HashSet,TreeSet和LinkedHashSet的區別

1. Set接口

  • Set不容許包含相同的元素,若是試圖把兩個相同元素加入同一個集合中,add方法返回false。
  • Set判斷兩個對象相同不是使用==運算符,而是根據equals方法。也就是說,只要兩個對象用equals方法比較返回true,Set就不會接受這兩個對象。

2. HashSet

HashSet有如下特色:java

  • 不能保證元素的排列順序,順序有可能發生變化
  • 不是同步的,集合元素能夠是null

當向HashSet集合中存入一個元素時,HashSet會調用該對象的hashCode()方法來獲得該對象的hashCode值,而後根據hashCode值來決定該對象在HashSet中存儲位置。簡單的說,HashSet集合判斷兩個元素相等的標準是兩個對象經過equals方法比較相等,而且兩個對象的hashCode()方法返回值相等。算法

注意,若是要把一個對象放入HashSet中,重寫該對象對應類的equals方法,也應該重寫其hashCode()方法。其規則是若是兩個對象經過equals方法比較返回true時,其hashCode也應該相同。數組

3. LinkedHashSet

  • LinkedHashSet與HashSet的不一樣之處在於,LinkedHashSet維護着一個運行於全部條目的雙重連接列表。此連接列表定義了迭代順序,該迭代順序可爲插入順序或是訪問順序。
  • LinkedHashSet集合一樣是根據元素的hashCode值來決定元素的存儲位置。

4. TreeSet

  • TreeSet是SortedSet接口的惟一實現類,TreeSet能夠確保集合元素處於排序狀態。
  • TreeSet支持兩種排序方式,天然排序和定製排序,其中天然排序爲默認的排序方式。向TreeSet中加入的應該是同一個類的對象。
  • TreeSet判斷兩個對象不相等的方式是兩個對象經過equals方法返回false,或者經過CompareTo方法比較沒有返回0

天然排序根據排序元素的CompareTo(Object obj)方法來比較元素之間大小關係,而後將元素按照升序排列。
Java提供了一個Comparable接口,該接口裏定義了一個compareTo(Object obj)方法,該方法返回一個整數值,實現了該接口的對象就能夠比較大小。
obj1.compareTo(obj2)方法若是返回0,則說明被比較的兩個對象相等,若是返回一個正數,則代表obj1大於obj2,若是是負數,則代表obj1小於obj2。
若是咱們將兩個對象的equals方法老是返回true,則這兩個對象的compareTo方法返回應該返回0。若是要定製排序,應該使用Comparator接口,實現int compare(To1,To2)方法安全

5. 性能分析

  • HashSet用的哈希表,開一個大數組,用哈希值映射到下標上,會有衝突,只有裝填因子小的時候性能纔好;
  • HashSet要留額外空間,佔內存大,數據頻繁插入的時候可能會不斷觸發Array Copy,可是讀寫性能通常很快;
  • TreeSet底層用紅黑樹實現,讀寫性能差一些,但不存在Array Copy問題,而且不佔用額外的不存儲數據的空間;
  • 通常來講空間換時間或者時間換空間。HashSet的查找代價爲O(1),TreeSet爲O(logn);
  • TreeSet的設計自己不是爲了空間時間的問題,而是爲了有序。所以它的插入及查找操做的代價都大於HashSet;
  • HashSet性能要好於TreeSet(特別是最經常使用的添加、查詢元素等操做),由於TreeSet須要額外的紅黑樹算法維護集合元素的次序。多線程

  • LinkedHashSet對於普通的插入,刪除操做比HashSet要略微慢一點,這是由維護鏈表所帶來的額外開銷形成的,但因爲有了鏈表,遍歷LinkedHashSet會更快;
  • EnumSet是全部Set實現類中性能最好的,但它只能保存同一個枚舉類的枚舉值做爲集合元素;併發

6. 線程安全

注意:Set的三個實現類HashSet、TreeSet和EnumSet都是線程不安全的。
若是有多個線程同事訪問一個Set集合,而且有超過一個線程修改了該Set集合,則必須手動保證該Set集合的同步。工具

  • 解決:一般經過Collections工具類的synchronizedSortedSet方法來「包裝」該Set集合。
  • 注意:此操做最好在建立時進行,以防止對Set集合的意外非同步訪問。
  • 示例:SortedSet set = Collections.synchronizedSortedSet(new TreeSet());

Collections 工具類其餘同步方法:性能

  • synchronizedCollection(Collection<T> c)
    這個方法返回一個同步的(線程安全的)集合的指定集合的支持。
  • synchronizedList(List<T>list)
    這個方法返回由指定列表支持的同步(線程安全的)列表。
  • synchronizedMap(Map<K,V> m)
    這個方法返回一個同步的(線程安全)由指定映射支持。
  • synchronizedSet(Set<T> s)
    這個方法返回一個同步的(線程安全的)集由指定set支持。
  • synchronizedSortedMap(SortedMap<K,V> m)
    這個方法返回一個同步的(線程安全的)有序映射所指定的有序映射支持
  • synchronizedSortedSet(SortedSet<T> s)
    這個方法返回一個同步的(線程安全的)有序set由指定的有序set支持。
  • synchronizedNavigableMap(NavigableMap<K,V> m)(JDK1.8)
  • synchronizedNavigableSet(NavigableSet<T> s)(JDK1.8)

7. 快速失敗

什麼是集合迭代器快速失敗行爲?以ArrayList爲例,在多線程併發狀況下,若是有一個線程在修改ArrayList集合的結構(插入、移除...),而另外一個線程正在用迭代器遍歷讀取集合中的元素,此時將拋出ConcurrentModificationException異常當即中止迭代遍歷操做,而不須要等到遍歷結束後再檢查有沒有出現問題;線程

  • Collection類返回一個Iterator以後,其實會建立一個指向原來對象的單鏈索引表,當原來的對象數量發生變化的時候,這個單鏈索引表的內容不會同步改變,因此當索引指針日後移動的時候,找不到要找的對象,就會按照fail-fast原則(快速失敗原則),Iterator立刻跑出java.util.ConcurrentModificationException異常。換一個說法,也就是,在Iterator工做的時候,是不容許被迭代的對象改變的。
  • 避免拋出這個異常的方法是:不使用Collection自身的remove()方法,而使用Iterator自己的方法remove()來刪除對象,由於這樣子能夠刪掉原對象,同時當前迭代對象的索引也獲得同步。
相關文章
相關標籤/搜索