Set接口
什么是Set,就是不包含重復元素的集合。
???? Set是一種不包括重復元素的Collection。它維持它自己的內部排序,所以隨機訪問沒有任何意義。與List一樣,它同樣允許null的存在但是僅有一個。由于Set接口的特殊性,所有傳入Set集合中的元素都必須不同,同時要注意任何可變對象,如果在對集合中元素進行操作時,導致e1.equals(e2)==true,則必定會產生某些問題。Set接口有三個具體實現類,分別是散列集HashSet、鏈式散列集LinkedHashSet和樹形集TreeSet。
???? Set是一種不包含重復的元素的Collection,無序,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。需要注意的是:雖然Set中元素沒有順序,但是元素在set中的位置是由該元素的HashCode決定的,其具體位置其實是固定的。
???? 此外需要說明一點,在set接口中的不重復是有特殊要求的。
???? 舉一個例子:對象A和對象B,本來是不同的兩個對象,正常情況下它們是能夠放入到Set里面的,但是如果對象A和B的都重寫了hashcode和equals方法,并且重寫后的hashcode和equals方法是相同的話。那么A和B是不能同時放入到Set集合中去的,也就是Set集合中的去重和hashcode與equals方法直接相關。?
為了更好地理解,請看下面的例子:
public class Test{ public static void main(String[] args) { Set<String> set=new HashSet<String>(); set.add("Hello"); set.add("world"); set.add("Hello"); System.out.println("集合的尺寸為:"+set.size()); System.out.println("集合中的元素為:"+set.toString()); } }
運行結果:
集合的尺寸為:2
集合中的元素為:[world, Hello]
分析:由于String類中重寫了hashcode和equals方法,用來比較指向的字符串對象所存儲的字符串是否相等。所以這里的第二個Hello是加不進去的。
再看一個例子:
public class TestSet {public static void main(String[] args){Set<String> books = new HashSet<String>();//添加一個字符串對象books.add(new String("Struts2權威指南"));//再次添加一個字符串對象,//因為兩個字符串對象通過equals方法比較相等,所以添加失敗,返回falseboolean result = books.add(new String("Struts2權威指南"));System.out.println(result);//下面輸出看到集合只有一個元素 System.out.println(books); } }
運行結果:
false
[Struts2權威指南]
說明:程序中,book集合兩次添加的字符串對象明顯不是一個對象(程序通過new關鍵字來創建字符串對象),當使用==運算符判斷返回false,使用equals方法比較返回true,所以不能添加到Set集合中,最后只能輸出一個元素。
以下基于JDK1.9對Set的描述:
public interface Set<E>
extends Collection<E>
A collection that contains no duplicate elements. More formally, sets contain no pair of elements?e1
?and?e2
?such that?e1.equals(e2)
, and at most one null element. As implied by its name, this interface models the mathematical?set?abstraction.
一個不包含重復元素的集合。更正式地說,集合中不包含e1和e2元素,比如e1.equals(e2),最多只包含一個空元素。正如它的名字所暗示的,這個接口模擬了數學集合的抽象。
The?Set
?interface places additional stipulations, beyond those inherited from the?Collection
?interface, on the contracts of all constructors and on the contracts of the?add
,?equals
?and?hashCode
?methods. Declarations for other inherited methods are also included here for convenience. (The specifications accompanying these declarations have been tailored to the?Set
?interface, but they do not contain any additional stipulations.)
Set接口附加了額外的規定,超出了從集合接口繼承的、所有構造函數的契約以及add、equals和hashCode方法的契約。為了方便起見,這里還包括了其他繼承方法的聲明。(伴隨這些聲明的規范已經針對Set接口進行了調整,但是它們不包含任何額外的規定。)
The additional stipulation on constructors is, not surprisingly, that all constructors must create a set that contains no duplicate elements (as defined above).
對于構造函數的附加規定,并不奇怪,所有的構造函數都必須創建一個不包含重復元素的集合(如上所述)。
Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects?equals
?comparisons while the object is an element in the set. A special case of this prohibition is that it is not permissible for a set to contain itself as an element.
注意:如果可變對象被用作設置元素,那么必須執行非常小心的操作。一組的行為沒有指定如果一個對象的值改變的方式影響=比較對象是一組元素。這個禁止的一種特殊情況是不允許包含一組本身為一個元素。
Some set implementations have restrictions on the elements that they may contain. For example, some implementations prohibit null elements, and some have restrictions on the types of their elements. Attempting to add an ineligible element throws an unchecked exception, typically?NullPointerException
?or?ClassCastException
. Attempting to query the presence of an ineligible element may throw an exception, or it may simply return false; some implementations will exhibit the former behavior and some will exhibit the latter. More generally, attempting an operation on an ineligible element whose completion would not result in the insertion of an ineligible element into the set may throw an exception or it may succeed, at the option of the implementation. Such exceptions are marked as "optional" in the specification for this interface.
一些set實現對它們可能包含的元素有限制。例如,有些實現禁止零元素,有些實現對其元素的類型有限制。試圖添加一個不合格的元素會拋出一個未經檢查的異常,通常是NullPointerException或ClassCastException。試圖查詢不合格元素的存在可能會拋出異常,或者它可能只是返回false;一些實現將展示前者的行為,而有些實現將展示后者。更一般的情況是,嘗試在不合格的元素上進行操作,其完成不會導致將不合格的元素插入到集合中,這可能會拋出異常,或者在實現的選項中可能成功。在該接口的規范中,這些異常被標記為“可選”。
(1)HashSet
???? HashSet 是一個沒有重復元素的集合。它是由HashMap實現的,不保證元素的順序(這里所說的沒有順序是指:元素插入的順序與輸出的順序不一致),而且HashSet允許使用null 元素。HashSet是非同步的,如果多個線程同時訪問一個哈希set,而其中至少一個線程修改了該set,那么它必須保持外部同步。?HashSet按Hash算法來存儲集合的元素,因此具有很好的存取和查找性能。
HashSet的實現方式大致如下,通過一個HashMap存儲元素,元素是存放在HashMap的Key中,而Value統一使用一個Object對象。
HashSet使用和理解中容易出現的誤區:
a.HashSet中存放null值
? HashSet中是允許存入null值的,但是在HashSet中僅僅能夠存入一個null值。
b.HashSet中存儲元素的位置是固定的
? HashSet中存儲的元素的是無序的,這個沒什么好說的,但是由于HashSet底層是基于Hash算法實現的,使用了hashcode,所以HashSet中相應的元素的位置是固定的。
??
c.必須小心操作可變對象(Mutable Object)。如果一個Set中的可變元素改變了自身狀態導致Object.equals(Object)=true將導致一些問題。
(2)LinkedHashSet
????? LinkedHashSet繼承自HashSet,其底層是基于LinkedHashMap來實現的,有序,非同步。LinkedHashSet集合同樣是根據元素的hashCode值來決定元素的存儲位置,但是它同時使用鏈表維護元素的次序。這樣使得元素看起來像是以插入順序保存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的添加順序訪問集合的元素。
(3)TreeSet
???? TreeSet是一個有序集合,其底層是基于TreeMap實現的,非線程安全。TreeSet可以確保集合元素處于排序狀態。TreeSet支持兩種排序方式,自然排序和定制排序,其中自然排序為默認的排序方式。當我們構造TreeSet時,若使用不帶參數的構造函數,則TreeSet的使用自然比較器;若用戶需要使用自定義的比較器,則需要使用帶比較器的參數。
注意:TreeSet集合不是通過hashcode和equals函數來比較元素的.它是通過compare或者comparaeTo函數來判斷元素是否相等.compare函數通過判斷兩個對象的id,相同的id判斷為重復元素,不會被加入到集合中。