Java 集合框架1:Collection
集合框架
1.概述
集合 collection ,也稱為 container 容器,一個將多個物件組合成一個單元的物件。集合用於儲存、返回、操作以及聚合。集合框架是一個統一表示和管理集合的框架。它減少不必要的程式設計工作,提升程式的速度和質量、互操作性,減少重複設計。
介面、實現、聚合操作和演算法共同組成了 Java 的集合框架。
- 介面:表示集合的抽象資料型別。介面允許獨立於集合具體表示(實現)來操作集合。
- 實現:對集合介面的具體實現。
- 演算法:集合上的各種計算操作,演算法應該具有多型性,即同樣的方法可以在不同的實現下的集合介面下使用。
2.Collection
Collection 是集合框架最基本的介面,除了 Map 外,List、Queue、Set 類集合都實現了它。
基本方法
1.基礎操作:
int size();
boolean isEmpty();
boolean contains(Object o);
boolean add(E e);
boolean remove(Object o);
Iterator<E> iterator();
2.bulk 操作有:
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
-
void clear();
。
3.stream 流:
-
default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); }
-
default Stream<E> parallelStream() { return StreamSupport.stream(spliterator(), true); }
4.針對Array
,還有以下的額外操作:
Object[] toArray();
<T> T[] toArray(T[] a);
容器遍歷
遍歷容器主要有三種方法: 聚合操作、for-each、迭代器Iterator。
String joined = elements.stream()
.map(Object::toString)
.collect(Collectors.joining(", "));
for (Object o : collection)
System.out.println(o);
public interface Iterator<E> {
boolean hasNext();
E next();
void remove(); //optional
}
對於 Iterator 中的 remove 方法,只能在每次 next 方法之後至多呼叫一次,否則將會丟擲異常。使用迭代器的好處是可以對 collection 中的元素進行刪除。在迭代過程中,Iterator 的 remove 方法是唯一可以刪除元素的操作,如果在迭代過程中通過其他方式對元素有其他修改,將會丟擲異常。
在下面的兩種情況下使用 Iterator,而不是 for-each:
- 在迭代中需要刪除元素。
- 並行的對容器進行迭代操作。
static void filter(Collection<?> c) { for (Iterator<?> it = c.iterator(); it.hasNext(); ) if (!cond(it.next())) it.remove(); }
容器實現
集合的實現實現了集合介面的方法,主要包含:
- General-purpose,使用最廣泛,在日常場景中使用。
- Special-purpose,對於特殊場景設計,具有非標準的效能特點和使用限制,行為。
- Concurrent,支援併發。
- Wrapper,用於組合不同的實現,比如為 general-purpose 實現的集合新增特殊的功能。
- Convenience,最小實現,通過靜態工廠方法方法獲得,用於替代 general-purpose 的方便,有效的實現。
- Abstract,骨架實現,方便構成。
上面的實現都允許插入 null 元素;並且都不是執行緒安全的。具有快速失敗(fast-fail)的迭代器設計:當迭代容器時檢測到了並行修改時,迭代器將會立即丟擲異常,防止任意的、不可期的行為發生。它們都實現了 Serializable 介面,並且都實現了 public 的 clone 方法。遺留的 Vector 和 Hashtable 是執行緒安全的,但已經建議不再使用了。如果需要執行緒安全的容器,可以使用 Collections 中的 synchronized wrapper 將上面的實現轉化成執行緒安全的,另外在 Java 的 concurrent 包中,提供了更多高效能的併發容器,比如 BlockingQueue , ConcurrentHashMap 等。
元素排序
集合中某些實現類底層是按照一定規則進行儲存,如 TreeMap 、 TreeSet。這裡有兩種排序方式:自然排序與定製排序。
1.自然排序
Java 提供 Comparable 介面,其中有 compareTo 方法。使用自然排序,需要容器中的元素實現 Comparable 介面中的 compareTo 方法,集合的排序根據 compareTo 方法來比較大小進行排序。
Java 基本型別的包裝類都已實現 compareTo 方法。
對於 Comparable 介面的實現,最好符合以下事項:
- 符合
e1.compareTo(e2) == 0
和e1.equals(e2)
等價。 - 對 null 元素比較需要丟擲 NullPointerException。因為 null 不是任何類的例項。
- compareTo 返回負數、零、正數,分別表示小於、等於、大於,且滿足
sign(x.compareTo(y)) == -sign(y.compareTo(x))
。 - compareTo 的實現滿足傳遞性:
x.compareTo(y) == 0
=>sign(x.compareTo(z)) = sign(y.compareTo(z))
。
2.定製排序
如果需要按特殊規則排序或者元素本身不具備比較性時,需要使用定製排序。
定製排序需要一個實現 Comparator 介面的 compare 方法的實現類,並在建立介面時將其作為引數傳遞過去。示例如下:
public class MyComparator implements Comparator {
@Override
public int compare(Object o1, Object o2) {
// 指定排序規則
return 0;
}
}
TreeSet tree = new TreeSet(new MyComparator());
3.Collections
collections 是作為 collection 的工具實現類。常用方法示例如下:
包裝器實現(Wrapper Implementations)
collections 的包裝器是裝飾器模式來實現的,包裝器並不提供公用類,都只提供靜態的工廠方法。
1.同步包裝器(Synchronization Wrappers)
同步包裝器為非執行緒安全的 collection 提供同步,通過以下方法,返回對應實現類的同步版本類:
上面的每一個方法返回執行緒安全的基於指定 collection 的同步 collection。
下面以 synchronizedList 方法裝飾 ArrayList 為例,使用以下方式返回執行緒安全的 SynchronizedRandomAccessList:
List<Type> list = Collections.synchronizedList(new ArrayList<Type>());
ArrayList 實現了 RandomAccess 介面,應當返回 SynchronizedRandomAccessList ,而 SynchronizedRandomAccessList 繼承 SynchronizedList ,實際上的同步實現在 SynchronizedList 中。(如果裝飾 LinkedList,因為未實現 RandomAccess 介面,直接返回 SynchronizedList。)
SynchronizedList 類中,所有方法內部添加了 synchronized 來同步程式碼塊。
注意:SynchronizedList 只實現了與 ArrayList 實現的介面,對於 ArrayList 提供的額外方法,如 ensureCapacity,無法呼叫。其它同步包裝器也如此。
2.不可變包裝器(Unmodified Wrappers)
不可變包裝器將 collection 進行改變的方法實現為丟擲 UnsupportedOperationException,來保證 collection 不可變或只讀,通過以下方法,返回對應實現類的不可變版本類:
Unmodified Wrappers 的實現方式與 Synchronization Wrappers 實現類似,都採用了裝飾器模式。以返回的 UnmodifiableList 為例,對於原本修改的方法,“重寫”為丟擲異常:
Empty 物件
Collections 以下方法返回空的容器:
它主要用於取代 null 的情況,當某個方法需要一個容器作為引數且不能為 null,你不想提供任何值的時候,就建立一個空的 collection:
tourist.declarePurchases(Collections.emptySet());
其實現方式是返回對應的 Empty 物件,以 emptyList 為例:
可以看出,每次呼叫 emptyList 時,返回的都是同一個 EmptyList 物件,以達到共用的目的。
不可變單例 Set(Immutable Singleton Set)
singleton 用於建立只包含單個指定元素的 Set。常用的就是移除集合中某個元素:
c.removeAll(Collections.singleton(e));
另外一個類似的就是移除 Map 中指定的 value:
job.values().removeAll(Collections.singleton(LAWYER));
不可變多副本列表(Immutable Multiple-Copy List)
nCopies 方法可以建立同個物件的多個副本的不可變 List 。比如建立具有 1000 個 Type 型別的 null 物件:
List<Type> list = new ArrayList<Type>(Collections.nCopies(1000, (Type) null);
第二個主要的用法是在List後面追加:
lovablePets.addAll(Collections.nCopies(69, "fruit bat"));
元素排序
sort 方法實現了對 List 進行排序,其實歸根到底還是呼叫了 Arrays 的排序演算法。
Arrays 的 mergeSort 用到了快速排序演算法,其比較大小的依據為 Comparable 介面中的 compareTo 方法。
所以呼叫 sort 方法時,需要保證集合中的元素實現了 Comparable 介面:
4.Stream API
Stream 是 Java 8 中處理集合的關鍵抽象概念,它可以指定對集合進行的操作,可以執行非常複雜的查詢、過濾和對映資料等操作。
它具有如下特點:
- Stream 自己不會儲存元素。
- Stream 不會改變源物件,相反,它們會返回一個持有結果的新 Stream。
- Stream 操作是延遲執行的,這意味這它們會等到需要結果的時候才執行。