Set擁有與Collection完全相同的接口,所以和兩種不同的List不同,它沒有什麼額外的功能。相反,Set完全就是一個Collection,只是具有不同的行為(這是實例和多形性最理想的應用:用於表達不同的行為)。在這裡,一個Set只允許每個對象存在一個實例(正如大家以後會看到的那樣,一個對象的“值”的構成是相當復雜的)。
Set(接口) 添加到Set的每個元素都必須是獨一無二的;否則Set就不會添加重復的元素。添加到Set裡的對象必須定義equals(),從而建立對象的唯一性。Set擁有與Collection完全相同的接口。一個Set不能保證自己可按任何特定的順序維持自己的元素
HashSet* 用於除非常小的以外的所有Set。對象也必須定義hashCode()
ArraySet 由一個數組後推得到的Set。面向非常小的Set設計,特別是那些需要頻繁創建和刪除的。對於小Set,與HashSet相比,ArraySet創建和反復所需付出的代價都要小得多。但隨著Set的增大,它的性能也會大打折扣。不需要HashCode()
TreeSet 由一個“紅黑樹”後推得到的順序Set(注釋⑦)。這樣一來,我們就可以從一個Set裡提到一個順序集合
⑦:直至本書寫作的時候,TreeSet仍然只是宣布,尚未正式實現。所以這裡沒有提供使用TreeSet的例子。
Set (interface)
Each element that you add to the Set must be unique; otherwise the Set doesn’t add the duplicate element. Objects added to a Set must define equals() to establish object uniqueness. Set has exactly the same interface as Collection. The Set interface does not guarantee it will maintain its elements in any particular order.
HashSet*
For Sets where fast lookup time is important. Objects must also define hashCode().
TreeSet
An ordered Set backed by a red-black tree. This way, you can extract an ordered sequence from a Set.
下面這個例子並沒有列出用一個Set能夠做的全部事情,因為接口與Collection是相同的,前例已經練習過了。相反,我們要例示的重點在於使一個Set獨一無二的行為:
//: Set1.java // Things you can do with Sets package c08.newcollections; import java.util.*; public class Set1 { public static void testVisual(Set a) { Collection1.fill(a); Collection1.fill(a); Collection1.fill(a); Collection1.print(a); // No duplicates! // Add another set to this one: a.addAll(a); a.add("one"); a.add("one"); a.add("one"); Collection1.print(a); // Look something up: System.out.println("a.contains(\"one\"): " + a.contains("one")); } public static void main(String[] args) { testVisual(new HashSet()); testVisual(new TreeSet()); } } ///:~
重復的值被添加到Set,但在打印的時候,我們會發現Set只接受每個值的一個實例。
運行這個程序時,會注意到由HashSet維持的順序與ArraySet是不同的。這是由於它們采用了不同的方法來保存元素,以便它們以後的定位。ArraySet保持著它們的順序狀態,而HashSet使用一個散列函數,這是特別為快速檢索設計的)。創建自己的類型時,一定要注意Set需要通過一種方式來維持一種存儲順序,就象本章早些時候展示的“groundhog”(土拔鼠)例子那樣。下面是一個例子:
//: Set2.java // Putting your own type in a Set package c08.newcollections; import java.util.*; class MyType implements Comparable { private int i; public MyType(int n) { i = n; } public boolean equals(Object o) { return (o instanceof MyType) && (i == ((MyType)o).i); } public int hashCode() { return i; } public String toString() { return i + " "; } public int compareTo(Object o) { int i2 = ((MyType) o).i; return (i2 < i -1 : (i2 == i 0 : 1)); } } public class Set2 { public static Set fill(Set a, int size) { for(int i = 0; i < size; i++) a.add(new MyType(i)); return a; } public static Set fill(Set a) { return fill(a, 10); } public static void test(Set a) { fill(a); fill(a); // Try to add duplicates fill(a); a.addAll(fill(new TreeSet())); System.out.println(a); } public static void main(String[] args) { test(new HashSet()); test(new TreeSet()); } } ///:~
對equals()及hashCode()的定義遵照“groundhog”例子已經給出的形式。在兩種情況下都必須定義一個equals()。但只有要把類置入一個HashSet的前提下,才有必要使用hashCode()——這種情況是完全有可能的,因為通常應先選擇作為一個Set實現。