文章目錄
1.5 Set1.5.1 概述
Java 中的Set和正好和數學上直觀的集(set)的概念是相同的。Set最大的特性就是不允許在其中存放的元素是重複的。根據這個特點,我們就可以使用Set 這個介面來實現前面提到的關於商品種類的儲存需求。Set 可以被用來過濾在其他集合中存放的元素,從而得到一個沒有包含重複新的集合。
1.5.2 常用方法
按照定義,Set 介面繼承 Collection 介面,而且它不允許集合中存在重複項。所有原始方法都是現成的,沒有引入新方法。具體的 Set 實作類別依賴添加的對象的 equals() 方法來檢查等同性。
我們簡單的描述一下各個方法的作用:
u public int size() :返回set中元素的數目,如果set包含的元素數大於Integer.MAX_VALUE,返回Integer.MAX_VALUE
u public boolean isEmpty() :如果set中不含元素,返回true
u public boolean contains(Object o) :如果set包含指定元素,返回true
u public Iterator iterator()
l 返回set中元素的迭代器
l 元素返回沒有特定的順序,除非set是提高了該保證的某些類的執行個體
u public Object[] toArray() :返回包含set中所有元素的數組
u public Object[] toArray(Object[] a) :返回包含set中所有元素的數組,返回數組的運行時類型是指定數組的運行時類型
u public boolean add(Object o) :如果set中不存在指定元素,則向set加入
u public boolean remove(Object o) :如果set中存在指定元素,則從set中刪除
u public boolean removeAll(Collection c) :如果set包含指定集合,則從set中刪除指定集合的所有元素
u public boolean containsAll(Collection c) :如果set包含指定集合的所有元素,返回true。如果指定集合也是一個set,只有是當前set的子集時,方法返回true
u public boolean addAll(Collection c) :如果set中中不存在指定集合的元素,則向set中加入所有元素
u public boolean retainAll(Collection c) :只保留set中所含的指定集合的元素(可選操作)。換言之,從set中刪除所有指定集合不包含的元素。 如果指定集合也是一個set,那麼該操作修改set的效果是使它的值為兩個set的交集
u public boolean removeAll(Collection c) :如果set包含指定集合,則從set中刪除指定集合的所有元素
u public void clear() :從set中刪除所有元素
“集合架構” 支援 Set 介面兩種普通的實現:HashSet 和 TreeSet以及LinkedHashSet。下表中是Set的常用實作類別的描述:
|
簡述 |
實現 |
操作特性 |
成員要求 |
Set |
成員不能重複 |
HashSet |
外部無序地遍曆成員。 |
成員可為任意Object子類的對象,但如果覆蓋了equals方法,同時注意修改hashCode方法。 |
TreeSet |
外部有序地遍曆成員; 附加實現了SortedSet, 支援子集等要求順序的操作 |
成員要求實現Comparable介面,或者使用Comparator構造TreeSet。成員一般為同一類型。 |
LinkedHashSet |
外部按成員的插入順序遍曆成員 |
成員與HashSet成員類似 |
在更多情況下,您會使用 HashSet 儲存重複自由的集合。同時HashSet中也是採用了Hash演算法的方式進行存取對象元素的。所以添加到 HashSet 的對象對應的類也需要採用恰當方式來實現 hashCode() 方法。雖然大多數系統類別覆蓋了 Object 中預設的 hashCode() 實現,但建立您自己的要添加到 HashSet 的類時,別忘了覆蓋 hashCode()。
對於Set的使用,我們先以一個簡單的例子來說明:
import java.util.*;
public class HashSetDemo {
public static void main(String[] args) {
Set set1 = new HashSet();
if (set1.add("a")) {//添加成功
System.out.println("1 add true");
}
if (set1.add("a")) {//添加失敗
System.out.println("2 add true");
}
set1.add("000");//添加對象到Set集合中
set1.add("111");
set1.add("222");
System.out.println("集合set1的大小:"+set1.size());
System.out.println("集合set1的內容:"+set1);
set1.remove("000");//從集合set1中移除掉 "000" 這個對象
System.out.println("集合set1移除 000 後的內容:"+set1);
System.out.println("集合set1中是否包含000 :"+set1.contains("000"));
System.out.println("集合set1中是否包含111 :"+set1.contains("111"));
Set set2=new HashSet();
set2.add("111");
set2.addAll(set1);//將set1 集合中的元素全部都加到set2中
System.out.println("集合set2的內容:"+set2);
set2.clear();//清空集合 set1 中的元素
System.out.println("集合set2是否為空白 :"+set2.isEmpty());
Iterator iterator = set1.iterator();//得到一個迭代器
while (iterator.hasNext()) {//遍曆
Object element = iterator.next();
System.out.println("iterator = " + element);
}
//將集合set1轉化為數組
Object s[]= set1.toArray();
for(int i=0;i<s.length;i++){
System.out.println(s[i]);
}
}
}
程式執行的結果為:
1 add true
集合set1的大小:4
集合set1的內容:[222, a, 000, 111]
集合set1移除 000 後的內容:[222, a, 111]
集合set1中是否包含000 :false
集合set1中是否包含111 :true
集合set2的內容:[222, a, 111]
集合set2是否為空白 :true
iterator = 222
iterator = a
iterator = 111
222
a
111
從上面的這個簡單的例子中,我們可以發現,Set中的方法與直接使用Collection中的方法一樣。唯一需要注意的就是Set中存放的元素不能重複。
我們再看一個例子,來瞭解一下其它的Set的實作類別的特性:
package c08;
import java.util.*;
public class SetSortExample {
public static void main(String args[]) {
Set set1 = new HashSet();
Set set2 = new LinkedHashSet();
for(int i=0;i<5;i++){
//產生一個隨機數,並將其放入Set中
int s=(int) (Math.random()*100);
set1.add(new Integer( s));
set2.add(new Integer( s));
System.out.println("第 "+i+" 次隨機數產生為:"+s);
}
System.out.println("未排序前HashSet:"+set1);
System.out.println("未排序前LinkedHashSet:"+set2);
//使用TreeSet來對另外的Set進行重構和排序
Set sortedSet = new TreeSet(set1);
System.out.println("排序後 TreeSet :"+sortedSet);
}
}
該程式的一次執行結果為:
第 0 次隨機數產生為:96
第 1 次隨機數產生為:64
第 2 次隨機數產生為:14
第 3 次隨機數產生為:95
第 4 次隨機數產生為:57
未排序前HashSet:[64, 96, 95, 57, 14]
未排序前LinkedHashSet:[96, 64, 14, 95, 57]
排序後 TreeSet :[14, 57, 64, 95, 96]
從這個例子中,我們可以知道HashSet的元素存放順序和我們添加進去時候的順序沒有任何關係,而LinkedHashSet 則保持元素的添加順序。TreeSet則是對我們的Set中的元素進行排序存放。
一般來說,當您要從集合中以有序的方式抽取元素時,TreeSet 實現就會有用處。為了能順利進行,添加到 TreeSet 的元素必須是可排序的。 而您同樣需要對添加到TreeSet中的類對象實現 Comparable 介面的支援。對於Comparable介面的實現,在前一小節的Map中已經簡單的介紹了一下。我們暫且假定一棵樹知道如何保持 java.lang 封裝程式器類元素的有序狀態。一般說來,先把元素添加到 HashSet,再把集合轉換為 TreeSet 來進行有序遍曆會更快。這點和HashMap的使用非常的類似。
其實Set的實現原理是基於Map上面的。通過下面我們對Set的進一步分析大家就能更加清楚的瞭解這點了。
1.5.3 實現原理
Java中Set的概念和數學中的集合(set)一致,都表示一個集內可以存放的元素是不能重複的。
前面我們會發現,Set中很多實作類別和Map中的一些實作類別的使用上非常的相似。而且前面再講解Map的時候,我們也提到:Map中的“索引值對”,其中的“鍵”是不能重複的。這個和Set中的元素不能重複一致。我們以HashSet為例來分析一下,會發現其實Set利用的就是Map中“鍵”不能重複的特性來實現的。
先看看HashSet中的有哪些屬性:
再結合建構函式來看看:
通過這些方法,我們可以發現,其實HashSet的實現,全部的操作都是基於HashMap來進行的。我們看看是如何通過HashMap來保證我們的HashSet的元素不重複性的:
看到這個操作我們可以發現HashSet的巧妙實現:就是建立一個“索引值對”,“鍵”就是我們要存入的對象,“值”則是一個常量。這樣可以確保,我們所需要的儲存的資訊之是“鍵”。而“鍵”在Map中是不能重複的,這就保證了我們存入Set中的所有的元素都不重複。而判斷是否添加元素成功,則是通過判斷我們向Map中存入的“索引值對”是否已經存在,如果存在的話,那麼傳回值肯定是常量:PRESENT ,表示添加失敗。如果不存在,傳回值就為null 表示添加成功。
我們再看看其他的方法實現:
瞭解了這些後,我們就不難理解,為什麼HashMap中需要注意的地方,在HashSet中也同樣的需要注意。其他的Set的實作類別也是差不多的原理。
至此對於Set我們就應該能夠比較好的理解了。