Collection介面
Collection表示一組稱為其元素的物件,Collection
介面用於傳遞需要最大通用性的物件集合,例如,按照慣例,所有通用集合實現都有一個帶有Collection
引數的建構函式,此建構函式(稱為轉換建構函式)初始化新集合以包含指定集合中的所有元素,無論給定集合的子介面或實現型別如何,換句話說,它允許你轉換集合的型別。
例如,假設你有一個Collection<String> c
,它可以是List
、Set
或其他型別的Collection
,這個語法建立一個新的ArrayList
(List
介面的一個實現),初始化包含c
中的所有元素。
List<String> list = new ArrayList<String>(c);
或者 — 如果你使用的是JDK 7或更高版本 — 你可以使用菱形運算子:
List<String> list = new ArrayList<>(c);
Collection
介面包含執行基本操作的方法,例如int size()
、boolean isEmpty()
、boolean contains(Object element)
、boolean add(E element)
、boolean remove(Object element)
和Iterator<E> iterator()
。
它還包含對整個集合進行操作的方法,例如boolean containsAll(Collection<?> c)
、boolean addAll(Collection<? extends E> c)
、boolean removeAll(Collection<?> c)
、boolean retainAll(Collection<?> c)
和void clear()
。
還存在用於陣列操作的額外方法(諸如Object[] toArray()
和<T> T[] toArray(T[] a)
)。
在JDK 8及更高版本中,Collection
介面還公開方法Stream<E> stream()
和Stream<E> parallelStream()
,以從底層集合中獲取順序或並行流(有關使用流的更多資訊,請參閱聚合操作的課程)。
如果Collection
表示一組物件,Collection
介面可以滿足你的期望,它有告訴你集合中有多少元素(size
、isEmpty
)的方法,檢查給定物件是否在集合中的方法(contains
),從集合中新增和刪除元素的方法(add
、remove
),和在集合上提供迭代器的方法(iterator
)。
add
方法的定義已經足夠廣泛,因此對於允許重複的集合以及不重複的集合都有意義,它保證Collection
在呼叫完成後將包含指定的元素,並且如果Collection
因呼叫而更改,則返回true
。類似地,remove
方法旨在從Collection
中刪除指定元素的單個例項,假設它包含要開始的元素,並且如果結果修改了集合則返回true
。
遍歷集合
有三種遍歷集合的方法:(1)使用聚合操作(2)for-each構造(3)通過使用Iterators
。
聚合操作
在JDK 8及更高版本中,迭代集合的首選方法是獲取流並對其執行聚合操作,聚合操作通常與lambda表示式結合使用,以使程式設計更具表現力,使用更少的程式碼行,以下程式碼按順序遍歷一組形狀並列印出紅色物件:
myShapesCollection.stream()
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
同樣,你可以輕鬆地請求並行流,如果集合足夠大並且你的計算機具有足夠的核心,這可能是有意義的:
myShapesCollection.parallelStream()
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
使用此API收集資料的方法有很多種,例如,你可能希望將Collection
的元素轉換為String
物件,然後將它們連線起來,用逗號分隔:
String joined = elements.stream()
.map(Object::toString)
.collect(Collectors.joining(", "));
或者可以把所有員工的工資加起來:
int total = employees.stream()
.collect(Collectors.summingInt(Employee::getSalary)));
這些只是你可以使用流和聚合操作執行的一些示例,有關更多資訊和示例,請參閱聚合操作的課程。
集合框架一直提供許多所謂的“批量操作”作為其API的一部分,這些包括對整個集合進行操作的方法,例如containsAll
、addAll
、removeAll
等,不要將這些方法與JDK 8中引入的聚合操作混淆。新聚合操作與現有批量操作(containsAll
、addAll
等)之間的主要區別在於舊版本都是可變的,這意味著它們都修改了底層集合,相反,新的聚合操作不會修改底層集合。使用新的聚合操作和lambda表示式時,必須注意避免突變,以免在以後從並行流執行程式碼時引入問題。
for-each構造
for-each構造允許你使用for
迴圈簡明地遍歷集合或陣列 — 請參閱for語句,以下程式碼使用for-each構造在單獨的行上列印出集合的每個元素。
for (Object o : collection)
System.out.println(o);
迭代器
Iterator是一個使你可以遍歷集合並需要時有選擇地從集合中刪除元素的物件,通過呼叫iterator
方法獲得集合的Iterator
,以下是Iterator
介面。
public interface Iterator<E> {
boolean hasNext();
E next();
void remove(); //optional
}
如果迭代具有更多元素,則hasNext
方法返回true
,並且next
方法返回迭代中的下一個元素,remove
方法從底層Collection
中刪除next
返回的最後一個元素,對next
的每次呼叫只能呼叫remove
方法一次,如果違反此規則則丟擲異常。
請注意,Iterator.remove
是在迭代期間修改集合的唯一安全方法,如果在迭代進行過程中以任何其他方式修改底層集合,則不指定此行為。
使用Iterator
而不是for-each
構造,當你需要:
- 刪除當前元素,for-each構造隱藏了迭代器,因此你無法呼叫
remove
,因此,for-each構造不可用於過濾。 - 並行迭代多個集合。
以下方法說明如何使用Iterator
過濾任意Collection
— 即遍歷集合刪除特定元素。
static void filter(Collection<?> c) {
for (Iterator<?> it = c.iterator(); it.hasNext(); )
if (!cond(it.next()))
it.remove();
}
這段簡單的程式碼是多型的,這意味著無論實現如何,它都適用於任何Collection
,此示例演示了使用Java集合框架編寫多型演算法是多麼容易。
集合介面批量操作
批量操作對整個Collection
執行操作,你可以使用基本操作實現這些簡寫操作,但在大多數情況下,此類實現效率較低,以下是批量操作:
-
containsAll
— 如果目標Collection
包含指定Collection
中的所有元素,則返回true
。 -
addAll
— 將指定Collection
中的所有元素新增到目標Collection
。 -
removeAll
— 從目標Collection
中刪除它們也包含在指定Collection
中的所有元素。 -
retainAll
— 從目標Collection
中刪除所有未包含在指定Collection
中的元素,也就是說,它僅保留目標Collection
中也包含在指定Collection
中的那些元素。 -
clear
— 從集合中刪除所有元素。
如果在執行操作的過程中修改了目標Collection
,則addAll
、removeAll
和retainAll
方法都返回true
。
作為批量操作功能的一個簡單示例,請考慮以下語法,從Collection
c
中刪除指定元素e
的所有例項。
c.removeAll(Collections.singleton(e));
更具體地說,假設你要從Collection
中刪除所有null
元素。
c.removeAll(Collections.singleton(null));
這個語法使用Collections.singleton
,這是一個靜態工廠方法,它返回一個只包含指定元素的不可變Set
。
集合介面陣列操作
toArray
方法是作為集合和舊API之間的橋樑提供的,這些API期望輸入上的陣列,陣列操作允許將Collection
的內容轉換為陣列,沒有引數的簡單形式建立一個新的Object
陣列,更復雜的形式允許呼叫者提供陣列或選擇輸出陣列的執行時型別。
例如,假設c
是Collection
,以下程式碼段將c
的內容轉儲到新分配的Object
陣列中,該陣列的長度與c
中的元素數相同。
Object[] a = c.toArray();
假設已知c
只包含字串(可能因為c
的型別為Collection<String>
),以下程式碼段將c
的內容轉儲到新分配的String
陣列中,該陣列的長度與c
中的元素數相同。
String[] a = c.toArray(new String[0]);