集合框架大管家
Collections 是 JDK 提供的一個工具類,位于 java.util 包下,提供了一系列的靜態(tài)方法,方便我們對集合進行各種騷操作,算是集合框架的一個大管家。
PS:star 這種事,只能求,不求沒效果,鐵子們,《Java 程序員進階之路》在 GitHub 上已經(jīng)收獲了 455 枚星標,鐵子們趕緊去點點了,沖 500 star!
https://github.com/itwanger/toBeBetterJavaer
還記得我們前面講過的 Arrays 工具類嗎?可以回去溫習下。
Collections 的用法很簡單,在 Intellij IDEA 中敲完 Collections. 之后就可以看到它提供的方法了,大致看一下方法名和參數(shù)就能知道這個方法是干嘛的。

為了節(jié)省大家的學習時間,我將這些方法做了一些分類,并列舉了一些簡單的例子。
01、排序操作
reverse(List list):反轉(zhuǎn)順序shuffle(List list):洗牌,將順序打亂sort(List list):自然升序sort(List list, Comparator c):按照自定義的比較器排序swap(List list, int i, int j):將 i 和 j 位置的元素交換位置
來看例子:
List?list?=?new?ArrayList<>();
list.add("沉默王二");
list.add("沉默王三");
list.add("沉默王四");
list.add("沉默王五");
list.add("沉默王六");
System.out.println("原始順序:"?+?list);
//?反轉(zhuǎn)
Collections.reverse(list);
System.out.println("反轉(zhuǎn)后:"?+?list);
//?洗牌
Collections.shuffle(list);
System.out.println("洗牌后:"?+?list);
//?自然升序
Collections.sort(list);
System.out.println("自然升序后:"?+?list);
//?交換
Collections.swap(list,?2,4);
System.out.println("交換后:"?+?list);
輸出后:
原始順序:[沉默王二, 沉默王三, 沉默王四, 沉默王五, 沉默王六]
反轉(zhuǎn)后:[沉默王六, 沉默王五, 沉默王四, 沉默王三, 沉默王二]
洗牌后:[沉默王五, 沉默王二, 沉默王六, 沉默王三, 沉默王四]
自然升序后:[沉默王三, 沉默王二, 沉默王五, 沉默王六, 沉默王四]
交換后:[沉默王三, 沉默王二, 沉默王四, 沉默王六, 沉默王五]
02、查找操作
binarySearch(List list, Object key):二分查找法,前提是 List 已經(jīng)排序過了max(Collection coll):返回最大元素max(Collection coll, Comparator comp):根據(jù)自定義比較器,返回最大元素min(Collection coll):返回最小元素min(Collection coll, Comparator comp):根據(jù)自定義比較器,返回最小元素fill(List list, Object obj):使用指定對象填充frequency(Collection c, Object o):返回指定對象出現(xiàn)的次數(shù)
來看例子:
System.out.println("最大元素:"?+?Collections.max(list));
System.out.println("最小元素:"?+?Collections.min(list));
System.out.println("出現(xiàn)的次數(shù):"?+?Collections.frequency(list,?"沉默王二"));
//?沒有排序直接調(diào)用二分查找,結(jié)果是不確定的
System.out.println("排序前的二分查找結(jié)果:"?+?Collections.binarySearch(list,?"沉默王二"));
Collections.sort(list);
//?排序后,查找結(jié)果和預(yù)期一致
System.out.println("排序后的二分查找結(jié)果:"?+?Collections.binarySearch(list,?"沉默王二"));
Collections.fill(list,?"沉默王八");
System.out.println("填充后的結(jié)果:"?+?list);
輸出后:
原始順序:[沉默王二, 沉默王三, 沉默王四, 沉默王五, 沉默王六]
最大元素:沉默王四
最小元素:沉默王三
出現(xiàn)的次數(shù):1
排序前的二分查找結(jié)果:0
排序后的二分查找結(jié)果:1
填充后的結(jié)果:[沉默王八, 沉默王八, 沉默王八, 沉默王八, 沉默王八]
03、同步控制
HashMap 是線程不安全的,這個我們前面講到了。那其實 ArrayList 也是線程不安全的,沒法在多線程環(huán)境下使用,那 Collections 工具類中提供了多個 synchronizedXxx 方法,這些方法會返回一個同步的對象,從而解決多線程中訪問集合時的安全問題。

使用起來也非常的簡單:
SynchronizedList?synchronizedList?=?Collections.synchronizedList(list);
看一眼 SynchronizedList 的源碼就明白了,不過是在方法里面使用 synchronized 關(guān)鍵字加了一層鎖而已。
static?class?SynchronizedList<E>
????extends?SynchronizedCollection<E>
????implements?List<E>?{
????private?static?final?long?serialVersionUID?=?-7754090372962971524L;
????final?List?list;
????SynchronizedList(List?list)?{
????????super(list);
????????this.list?=?list;
????}
????public?E?get(int?index)?{
????????synchronized?(mutex)?{return?list.get(index);}
????}
????
????public?void?add(int?index,?E?element)?{
????????synchronized?(mutex)?{list.add(index,?element);}
????}
????public?E?remove(int?index)?{
????????synchronized?(mutex)?{return?list.remove(index);}
????}
}
那這樣的話,其實效率和那些直接在方法上加 synchronized 關(guān)鍵字的 Vector、Hashtable 差不多(JDK 1.0 時期就有了),而這些集合類基本上已經(jīng)廢棄了,幾乎不怎么用。
public?class?Vector<E>
????extends?AbstractList<E>
????implements?List<E>,?RandomAccess,?Cloneable,?java.io.Serializable
{
????public?synchronized?E?get(int?index)?{
????????if?(index?>=?elementCount)
????????????throw?new?ArrayIndexOutOfBoundsException(index);
????????return?elementData(index);
????}
????public?synchronized?E?remove(int?index)?{
????????modCount++;
????????if?(index?>=?elementCount)
????????????throw?new?ArrayIndexOutOfBoundsException(index);
????????E?oldValue?=?elementData(index);
????????int?numMoved?=?elementCount?-?index?-?1;
????????if?(numMoved?>?0)
????????????System.arraycopy(elementData,?index+1,?elementData,?index,
?????????????????????????????numMoved);
????????elementData[--elementCount]?=?null;?//?Let?gc?do?its?work
????????return?oldValue;
????}
}
正確的做法是使用并發(fā)包下的 CopyOnWriteArrayList、ConcurrentHashMap。這些我們放到并發(fā)編程時再講。
04、不可變集合
emptyXxx():制造一個空的不可變集合singletonXxx():制造一個只有一個元素的不可變集合unmodifiableXxx():為指定集合制作一個不可變集合
舉個例子:
List?emptyList?=?Collections.emptyList();
emptyList.add("非空");
System.out.println(emptyList);
這段代碼在執(zhí)行的時候就拋出錯誤了。
Exception?in?thread?"main"?java.lang.UnsupportedOperationException
?at?java.util.AbstractList.add(AbstractList.java:148)
?at?java.util.AbstractList.add(AbstractList.java:108)
?at?com.itwanger.s64.Demo.main(Demo.java:61)
這是因為 Collections.emptyList() 會返回一個 Collections 的內(nèi)部類 EmptyList,而 EmptyList 并沒有重寫父類 AbstractList 的 add(int index, E element) 方法,所以執(zhí)行的時候就拋出了不支持該操作的 UnsupportedOperationException 了。
這是從分析 add 方法源碼得出的原因。除此之外,emptyList 方法是 final 的,返回的 EMPTY_LIST 也是 final 的,種種跡象表明 emptyList 返回的就是不可變對象,沒法進行增傷改查。
public?static?final??List?emptyList()? {
????return?(List)?EMPTY_LIST;
}
public?static?final?List?EMPTY_LIST?=?new?EmptyList<>();
05、其他
還有兩個方法比較常用:
addAll(Collection super T> c, T... elements),往集合中添加元素disjoint(Collection> c1, Collection> c2),判斷兩個集合是否沒有交集
舉個例子:
List?allList?=?new?ArrayList<>();
Collections.addAll(allList,?"沉默王九","沉默王十","沉默王二");
System.out.println("addAll 后:"?+?allList);
System.out.println("是否沒有交集:"?+?(Collections.disjoint(list,?allList)???"是"?:?"否"));
輸出后:
原始順序:[沉默王二, 沉默王三, 沉默王四, 沉默王五, 沉默王六]
addAll 后:[沉默王九, 沉默王十, 沉默王二]
是否沒有交集:否
整體上,Collections 工具類作為集合框架的大管家,提供了一些非常便利的方法供我們調(diào)用,也非常容易掌握,沒什么難點,看看方法的注釋就能大致明白干嘛的。
不過,工具就放在那里,用是一回事,為什么要這么用就是另外一回事了。能不能提高自己的編碼水平,很大程度上取決于你到底有沒有去鉆一鉆源碼,看這些設(shè)計 JDK 的大師們是如何寫代碼的,學會一招半式,在工作當中還是能很快脫穎而出的。
恐怕 JDK 的設(shè)計者是這個世界上最好的老師了,文檔寫得不能再詳細了,代碼寫得不能再優(yōu)雅了,基本上都達到了性能上的極致。
可能有人會說,工具類沒什么鳥用,不過是調(diào)用下方法而已,但這就大錯特錯了:如果要你來寫,你能寫出來 Collections 這樣一個工具類嗎?
這才是高手要思考的一個問題。
這是《Java 程序員進階之路》專欄的第 64 篇。Java 程序員進階之路,風趣幽默、通俗易懂,對 Java 初學者極度友好和舒適??,內(nèi)容包括但不限于 Java 語法、Java 集合框架、Java IO、Java 并發(fā)編程、Java 虛擬機等核心知識點。
點擊上方名片,發(fā)送消息「03」 就可以獲取《Java 程序員進階之路》的 PDF 版了,一起成為更好的 Java 工程師。

