top
Loading...
深入理解CollectionsAPI
b>接口

Collection接口


一個Collection代表了一組對象,即它的元素。 Collection接口的主要用途是在需要最大通用性的地方傳遞對象集。例如,所有的通用對象集實現(它典型地實現了象Set或List這樣的Collection的子接口)按慣例都有一個獲取Collection參數的構造函數。該構造函數將一個新的Collection初始化以包含特定Collection中的所有元素,并允許調用者創建一個所期望的實現類型的Collection,它最初包含任意已知Collection中的全部元素,而不管它的子接口或實現類型。假設你有一個Collection, c, 它可能是一個List, 一個Set, 或某些其它種類的Collection. 下面一行程序則創建了一個新的ArrayList ( List接口的一個實現), 并在初始情況下包含c中的所有元素:

List l = new ArrayList(c);

Collection接口如下所示:

public interface Collection {

// Basic Operations

int size();

boolean isEmpty();

boolean contains(Object element);

boolean add(Object element); // Optional

boolean remove(Object element); // Optional

Iterator iterator();

// Bulk Operations

boolean containsAll(Collection c);

boolean addAll(Collection c); // Optional

boolean removeAll(Collection c); // Optional

boolean retainAll(Collection c); // Optional

void clear(); // Optional

// Array Operations

Object[] toArray();

Object[] toArray(Object a[]);

}

假設一個Collection代表了一組對象,則接口將會完成你所期待它們所干的工作。它用方法告知你在對象集中,有多少元素(size, isEmpty);檢查在對象集中是否包含給定的對象(contains);從對象集中增加和刪除元素(add, remove);以及在對象集上提供迭代(iterator)。

add方法經過了充分的一般性定義,因而它不僅對允許重復的對象集有意義,對不允許重復的對象集同樣也有意義。它保證Collection在調用完成后將包含特殊的元素,并在Collection發生改變時,返回一個true 。類似地,remove方法 ㄒ搴螅山獵ollection中的特殊元素的一個單獨的實例(a single instance)刪除(假定Collection包含該元素), 并在Collection發生更改時返回true 。

Iterators(迭代)

用iterator方法返回的對象應該受到特別的關注。 Iterator與Enumeration非常相似,但在兩個方面有所不同:

terator允許調用者在迭代過程中用精心設計的語法從底層次結構對象集中刪除元素

方法名已被改進

第一點是十分重要的:

在用一個Enumeration遍歷一個對象集的同時,沒有任何一種從對象集中刪除元素的安全方法。 這個操作的語義是不清楚的,并隨著實現的不同而有所改變。

Iterator接口如下所示:

public interface Iterator {

boolean hasNext();

Object next();

void remove(); // Optional

}

hasNext方法在功能上與Enumeration.hasMoreElements是相同的,而next方法在功能上與Enumeration.nextElement相同。remove方法從底層次結構Collection中刪除由next返回的最后一個元素。在每次調用next時, remove方法只能被調用一次, 并在違反上述規則時,扔出異常。請注意Iterator.remove是在迭代過程中更改一個對象集的唯一安全途徑;在迭代過程中,如果底層次結構對象集以任何其它方式被更改,該行為不被特別指明。

下面的小程序為你演示了如何使用一個Iterator來過濾一個Collection, 也就是,遍歷該對象集, 并刪除每一個不滿足某些條件的元素:

static void filter(Collection c) {

for (Iterator i = c.iterator(); i.hasNext(); )

if (!cond(i.next()))

i.remove();


}

在閱讀這個簡單的代碼時,應注意兩件事:

這些代碼是多態的(polymorphic): 它可以應用于任何支持元素刪除的Collection , 而不用考慮實現。 因而在對象集結構下編寫一個多態算法是如此簡單!

使用Enumeration而不是Iterator來編寫也許是不可能的。因為當用一個Enumeration來遍歷一個對象集時,沒有一種安全的方法可以將一個元素從對象集中刪除。

Bulk Operations(批量操作)

bulk operations可一次完成對全部Collection的某些操作。 使用上面所描述的操作,批量操作的每一步都可被模擬(或許這是缺少效率的),從這個意義上講,批量操作是一種速記法。

containsAll: 如果目標Collection包含特定Collection (c)中的全部元素,則返回true

addAll: 將特定Collection中的所有元素添加到目標Collection中.

removeAll: 從目標Collection中刪除所有同時也包含在特定Collection中的元素

retainAll: 從目標Collection中刪除所有在特定Collection中不包含的元素。也就是說,在目標Collection中僅保留那些在特定Collection中也同時保留的元素。

clear: 從Collection中刪除所有元素。 如果目標Collection在執行操作的過程中被更改,則addAll, removeAll, 和retainAll方法均返回true。

作為顯示批量操作的能量的一個簡單示例,請看下面的慣用程序,它將一個特定元素, e的全部實例從Collection, c中刪除:

c.removeAll(Collections.singleton(e));

更特別地,假設你要從一個Collection中刪除所有空元素:

c.removeAll(Collections.singleton(null));

這個慣用程序使用了Collections.singleton, 它是一個返回只包含特定元素且永恒不變的Set的靜態方法。

Array Operations(數組操作)

toArray方法可作為對象集 和期望將數組作為輸入的舊的API之間的一個橋梁,它允許將一個Collection的內容轉換為一個數組。沒有參數的簡單形式創建了一個新的Object(對象)數組。更復雜的形式允許調用者提供一個數組或選擇這個輸出數組的運行時類型。 例如,假設c是一個Collection。 如下程序可將c的內容傾卸到一個新分配的Object數組中,數組的長度與c中元素的數量相同。

Object[] a = c.toArray();

假設已知c僅包含字符串。下列程序則將c的內容傾卸到一個新分配String數組中,數組的長度與c中元素的數量相同。

String[] a = (String[]) c.toArray(new String[0]);


Set接口

Set是一個不包含重復元素的Collection。Set接口塑造了抽象的set數學模型。Set接口不包含除從Collection中繼承的方法之外的任何其它方法。它禁止使用重復元素,同時它還增加了對equals和hashCode操作行為的限制,允許不同實現類型的Set對象可做比較。如果兩個Set對象包含同樣的元素,則它們是相等的。

Set接口如下所示:

public interface Set {

// Basic Operations

int size();

boolean isEmpty();

boolean contains(Object element);

boolean add(Object element); // Optional

boolean remove(Object element); // Optional

Iterator iterator();

// Bulk Operations

boolean containsAll(Collection c);

boolean addAll(Collection c); // Optional

boolean removeAll(Collection c); // Optional

boolean retainAll(Collection c); // Optional

void clear(); // Optional

// Array Operations

Object[] toArray();

Object[] toArray(Object a[]);

}

JDK包含兩個通用Set實現。一個是HashSet, 它將它的元素存儲于哈希表中,它是一種最好的實現;另一個是TreeSet, 它將它的元素存儲在一個紅-黑樹上,它保證了迭代順序。

這是一個簡單但實用的Set慣用程序。 假設你有一個Collection, c, 并且你要創建另一個包含相同元素的Collection,但是所有重復元素已被清除。下面的程序給出了一個小技巧:

Collection noDups = new HashSet(c);

它是通過創建一個Set(按定義,Set不能包含重復元素)來達到目的的,這個Set初始包含c中的所有元素。

基本操作

size操作返回Set中元素的數量(它的集的勢(cardinality))。 isEmpty方法所完成的工作正如你從字面上理解的一樣。add方法將特定元素添加到Set中(如果該元素不在Set之中), 并返回一個表示該元素是否已被添加的布爾值。 類似地, remove方法從Set中刪除特定元素(如果該元素存在于Set之中), 并返回表示該元素是否存在的布爾值。iterator方法返回一個在Set上的Iterator 。

以下是一個小程序, 它從它的參數列表中獲得詞匯并打印出任何重復詞匯、不同詞匯的數量以及刪除了重復詞匯的詞匯列表:

import java.util.*;

public class FindDups {

public static void main(String args[]) {

Set s = new HashSet();

for (int i=0; i$#@60; args.length; i++)

if (!s.add(args[i]))

System.out.println("Duplicate detected: "+args[i]);

System.out.println(s.size()+" distinct words detected: "+s);
}

}

現在讓我們來運行這個程序:

% java FindDups i came i saw i left

Duplicate detected: i

Duplicate detected: i

4 distinct words detected: [came, left, saw, i]

請注意, 該例子中的代碼總是通過它的接口類型(Set )來引用對象集 , 而不是通過它的實現類型(HashSet)。這是一種被極力推薦的編程方法, 因為它給了你僅僅通過改變構造函數就可以改變實現的靈活性。如果用來存儲一個對象集 的變量, 或用來傳遞對象集 的參數被聲明為是對象集 的實現類型而不是它的接口類型的, 那么所有這樣的變量和參數必須被改變以改變對象集 的實現類型。 進一步來講, 如果程序中使用了任何在原始實現類型中存在而新的實現中沒有的非標準操作, 那么將不能保證程序的正常運行。僅僅通過對象集的接口來引用對象集使你保持了誠實的作風, 因為從某種意義上講, 它使你不能使用任何非標準操作。

在上述例子中的Set的實現類型是HashSet, 它不保證Set中的元素的順序。如果你需要你的程序按字母順序打印詞匯, 你所要做的全部工作就是將set的實現類型從HashSet改變為TreeSet。這一小小的改動會使以前例子中的命令行生成如下輸出:

% java FindDups i came i saw i left

Duplicate word detected: i

Duplicate word detected: i

4 distinct words detected: [came, i, left, saw]

批量操作(Bulk Operations)

批量操作特別適合于Sets: 它們執行標準集合代數(set-algebraic) 操作。假設s1和s2是Set, 以下是批量操作將做的工作:

s1.containsAll(s2): 如果s2是s1的一個subset , 返回真 (例如, 如果set s2包含s1中的所有元素, 則set s1是s2的一個subset)

s1.addAll(s2): 將s1轉換到s1和s2的并集( union ) (兩個sets的并集包含s1或者s2的元素)

s1.retainAll(s2): 將s1轉換到s1和s2的交集( intersection ) (兩個sets的交集僅包含兩個sets中的共同的元素)

s1.removeAll(s2): 將s1轉換到s1和s2的(不對稱)差集((asymmetric) set difference) (例如, s1 - s2的差集包含在s1中但不包含在s2中的所有元素)

為了非破壞性地(不更改任何集)計算并集、交集或差集,調用者必須在調用批量操作之前,拷貝一個set , 作為其結果的慣用程序如下所示:

Set union = new HashSet(s1);

union.addAll(s2);

Set intersection = new HashSet(s1);

intersection.retainAll(s2);

Set difference = new HashSet(s1); difference.removeAll(s2);

上述慣用程序的結果Set的實現類型是HashSet, 正如上面所提到的那樣,它是JDK中最全面的Set實現。然而,任何通用Set實現都可能被替代。

讓我們重新看看上述例子中的FindDups 。 假設你要知道在參數列表中哪個詞匯僅出現了一次,哪個詞匯出現的次數多于一次,但是你不希望將重復詞匯重復地打印。你可以通過生成兩個sets來實現, 一個包含參數列表中的每一個詞匯,另一個僅包含重復詞匯,僅出現一次的詞匯即兩個sets的差集, 我們已經了解了差集的計算方法。

import java.util.*;

public class FindDups2 {

public static void main(String args[]) {

Set uniques = new HashSet();

Set dups = new HashSet();

for (int i=0; i$#@60; args.length; i++)

if (!uniques.add(args[i]))

dups.add(args[i]);

uniques.removeAll(dups); // Destructive set-difference

System.out.println("Unique words: " + uniques);

System.out.println("Duplicate words: " + dups);

}

}

現在,讓我們運行用我們以前使用過的相同的參數列表修改的程序:

% java FindDups2 i came i saw i left

Unique words: [came, left, saw]

Duplicate words: [i]

一個較少使用的 集合代數操作是對稱差集:它的元素包含在兩個特定sets的某一個當中, 但不是在兩個之中都包含。下列代碼非破壞性地計算了對稱差集:

Set symmetricDiff = new HashSet(s1);

symmetricDiff.addAll(s2);

Set tmp = new HashSet(s1);

tmp.retainAll(s2);

symmetricDiff.removeAll(tmp);

數組操作

數組操作在Sets方面所做的事情與在其它Collection上所做的事情基本相同。有關描述見Collection接口lesson。

什么是Collection?

核心 對象集 接口(核心對象集接口)是用來操作對象集并將它們從一個方法傳遞到另一個方法的接口,這些接口的基本目標是允許對它們所表示的對象集進行獨立于具體細節的操作。核心 對象集 接口是對象集 架構的核心和靈魂。當你理解了如何使用這些接口時,你就了解了有關架構的大部分內容。核心 對象集 接口如下所示:




核心對象集接口形成了一個hierarchy(層次結構): Set是一種特殊的Collection, 而SortedSet是一種特殊的Set, 以此類推。請注意層次結構由兩個明顯不同的樹構成: Map不是一個真正的Collection.

為保持可管理的核心 對象集 接口的數量,JDK不為每個對象集類型的每個變異提供單獨的接口(其中可能的變異是永恒不變的、固定大小的和只能增加的),而將對每個接口的更改操作指定為可選擇的(optional): 一個已知的實現可能不支持某些這樣的操作。如果調用了一個不被支持的操作,對象集則扔出一個例外UnsupportedOperationException。哪個選項操作可被那些實現所支持,是由實現用文檔來加以說明的。所有JDK的通用實現都支持全部選項操作。

以下四個部分將講述如何使用四個基本核心 對象集 接口的問題。特別是講述了使你可高效使用這些接口的慣用程序。

Collection

Collection接口是對象集層次結構的根。一個對象集代表了一組對象,這些對象被稱為它的元素。某些對象集實現允許重復元素存在,而有些則不允許;有些是經過排序的,而有些則未經排序。JDK不提供這個接口的任何直接的實現:它提供諸如Set和List這樣的更特殊的子接口的實現。這個接口是所有對象集所要實現的最小通用性的一般水準。Collection被用來傳遞對象集,并在期望得到最大通用性時操縱它們。

Set

Set是一個不包含重復元素的對象集。正象你可能期望的那樣,這個接口塑造了集合的抽象數學模型。它被用來表示就象是紙牌構成了洗牌手手中的牌、課程構成了學生的課程表以及過程運行于一個機器那樣的集合。

List

List是一個經過排序的對象集 (有時也稱作序列(sequence)). Lists可以包含重復元素。一個List用戶通常可以控制List中每個元素被插入的準確位置。 用戶可以通過它們的整數索引(位置)來訪問元素。如果你已經使用過Vector, 你應該熟悉List的一般風格。

Map

Map是一個將鍵映射為一個值的對象。Maps不包含重復鍵:每個鍵最多可映射一個值。如果你使用過Hashtable, 你應該熟悉Map的一般風格。

最后兩個核心 對象集 接口 (SortedSet和SortedMap) 只是Set和Map的排序版。為理解這些接口,你必須了解對象間是如何保持排序的。即使你沒有使用SortedSet或SortedMap的打算,但是如果你要對Lists排序,請閱讀以下章節。

對象排序

有兩種對象排序的方法: Comparable接口可按實現它的類自動提供自然排序, 而Comparator接口則將把對對象排序的控制權,全部交給程序員。請注意這些不是核心 對象集 接口, 而是底層次結構結構。

既然你已經了解了有關對象排序的全部內容,請看下面的最后兩個核心 對象集 接口:

SortedSet

SortedSet是一個將它的元素保持為上升順序的Set。有幾個附加操作被提供,以利用該排序。SortedSet接口一般被用在單詞列表和成員列表等方面。

SortedMap

SortedMap是一個將它的映射保持為上升鍵順序的Map。 它是SortedSet的Map對等物。SortedMap接口一般用在諸如詞典和電話簿那樣的應用程序中。
作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗