天天看點

集合的生産力工具類:Collections,我直呼好家夥。。

Collections 是 JDK 提供的一個工具類,位于 java.util 包下,提供了一系列的靜态方法,友善我們對集合進行各種騷操作,算是集合架構的一個大管家。

PS:star 這種事,隻能求,不求沒效果,鐵子們,《Java 程式員進階之路》在 GitHub 上已經收獲了 556 枚星标,鐵子們趕緊去點點了,沖 600 star!

https://github.com/itwanger/toBeBetterJavaer

還記得我們前面講過的 Arrays 工具類嗎?可以回去溫習下。

Collections 的用法很簡單,在 Intellij IDEA 中敲完 Collections. 之後就可以看到它提供的方法了,大緻看一下方法名和參數就能知道這個方法是幹嘛的。

集合的生産力工具類:Collections,我直呼好家夥。。
為了節省大家的學習時間,我将這些方法做了一些分類,并列舉了一些簡單的例子。

01、排序操作

reverse(List list):反轉順序

shuffle(List list):洗牌,将順序打亂

sort(List list):自然升序

sort(List list, Comparator c):按照自定義的比較器排序

swap(List list, int i, int j):将 i 和 j 位置的元素交換位置

來看例子:

List<String> list = new ArrayList<>();
list.add("沉默王二");
list.add("沉默王三");
list.add("沉默王四");
list.add("沉默王五");
list.add("沉默王六");
System.out.println("原始順序:" + list);
// 反轉
Collections.reverse(list);
System.out.println("反轉後:" + list);
// 洗牌
Collections.shuffle(list);
System.out.println("洗牌後:" + list);
// 自然升序
Collections.sort(list);
System.out.println("自然升序後:" + list);
// 交換
Collections.swap(list, 2,4);
System.out.println("交換後:" + list);      

輸出後:

原始順序:[沉默王二, 沉默王三, 沉默王四, 沉默王五, 沉默王六]

反轉後:[沉默王六, 沉默王五, 沉默王四, 沉默王三, 沉默王二]

洗牌後:[沉默王五, 沉默王二, 沉默王六, 沉默王三, 沉默王四]

自然升序後:[沉默王三, 沉默王二, 沉默王五, 沉默王六, 沉默王四]

交換後:[沉默王三, 沉默王二, 沉默王四, 沉默王六, 沉默王五]

02、查找操作

binarySearch(List list, Object key):二分查找法,前提是 List 已經排序過了

max(Collection coll):傳回最大元素

max(Collection coll, Comparator comp):根據自定義比較器,傳回最大元素

min(Collection coll):傳回最小元素

min(Collection coll, Comparator comp):根據自定義比較器,傳回最小元素

fill(List list, Object obj):使用指定對象填充

frequency(Collection c, Object o):傳回指定對象出現的次數

System.out.println("最大元素:" + Collections.max(list));
System.out.println("最小元素:" + Collections.min(list));
System.out.println("出現的次數:" + Collections.frequency(list, "沉默王二"));
// 沒有排序直接調用二分查找,結果是不确定的
System.out.println("排序前的二分查找結果:" + Collections.binarySearch(list, "沉默王二"));
Collections.sort(list);
// 排序後,查找結果和預期一緻
System.out.println("排序後的二分查找結果:" + Collections.binarySearch(list, "沉默王二"));
Collections.fill(list, "沉默王八");
System.out.println("填充後的結果:" + list);      

最大元素:沉默王四

最小元素:沉默王三

出現的次數:1

排序前的二分查找結果:0

排序後的二分查找結果:1

填充後的結果:[沉默王八, 沉默王八, 沉默王八, 沉默王八, 沉默王八]

03、同步控制

HashMap 是線程不安全的,這個我們前面講到了。那其實 ArrayList 也是線程不安全的,沒法在多線程環境下使用,那 Collections 工具類中提供了多個 synchronizedXxx 方法,這些方法會傳回一個同步的對象,進而解決多線程中通路集合時的安全問題。

集合的生産力工具類:Collections,我直呼好家夥。。

使用起來也非常的簡單:

SynchronizedList synchronizedList = Collections.synchronizedList(list);

看一眼 SynchronizedList 的源碼就明白了,不過是在方法裡面使用 synchronized 關鍵字加了一層鎖而已。

static class SynchronizedList<E>
    extends SynchronizedCollection<E>
    implements List<E> {
    private static final long serialVersionUID = -7754090372962971524L;
    final List<E> list;
    SynchronizedList(List<E> 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 關鍵字的 Vector、Hashtable 差不多(JDK 1.0 時期就有了),而這些集合類基本上已經廢棄了,幾乎不怎麼用。

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;
    }
}      

正确的做法是使用并發包下的 CopyOnWriteArrayList、ConcurrentHashMap。這些我們放到并發程式設計時再講。

04、不可變集合

emptyXxx():制造一個空的不可變集合

singletonXxx():制造一個隻有一個元素的不可變集合

unmodifiableXxx():為指定集合制作一個不可變集合

舉個例子:

List emptyList = Collections.emptyList();

emptyList.add("非空");

System.out.println(emptyList);

這段代碼在執行的時候就抛出錯誤了。

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 的内部類 EmptyList,而 EmptyList 并沒有重寫父類 AbstractList 的 add(int index, E element) 方法,是以執行的時候就抛出了不支援該操作的 UnsupportedOperationException 了。

這是從分析 add 方法源碼得出的原因。除此之外,emptyList 方法是 final 的,傳回的 EMPTY_LIST 也是 final 的,種種迹象表明 emptyList 傳回的就是不可變對象,沒法進行增傷改查。

public static final <T> List<T> emptyList() {

   return (List<T>) EMPTY_LIST;

}

public static final List EMPTY_LIST = new EmptyList<>();

05、其他

還有兩個方法比較常用:

addAll(Collection<? super T> c, T... elements),往集合中添加元素

disjoint(Collection<?> c1, Collection<?> c2),判斷兩個集合是否沒有交集

List<String> allList = new ArrayList<>();

Collections.addAll(allList, "沉默王九","沉默王十","沉默王二");

System.out.println("addAll 後:" + allList);

System.out.println("是否沒有交集:" + (Collections.disjoint(list, allList) ? "是" : "否"));

addAll 後:[沉默王九, 沉默王十, 沉默王二]

是否沒有交集:否

整體上,Collections 工具類作為集合架構的大管家,提供了一些非常便利的方法供我們調用,也非常容易掌握,沒什麼難點,看看方法的注釋就能大緻明白幹嘛的。

不過,工具就放在那裡,用是一回事,為什麼要這麼用就是另外一回事了。能不能提高自己的編碼水準,很大程度上取決于你到底有沒有去鑽一鑽源碼,看這些設計 JDK 的大師們是如何寫代碼的,學會一招半式,在工作當中還是能很快脫穎而出的。

恐怕 JDK 的設計者是這個世界上最好的老師了,文檔寫得不能再詳細了,代碼寫得不能再優雅了,基本上都達到了性能上的極緻。

可能有人會說,工具類沒什麼鳥用,不過是調用下方法而已,但這就大錯特錯了:如果要你來寫,你能寫出來 Collections 這樣一個工具類嗎?

這才是高手要思考的一個問題。

這是《Java 程式員進階之路》專欄的第 64 篇。Java 程式員進階之路,風趣幽默、通俗易懂,對 Java 初學者極度友好和舒适😘,内容包括但不限于 Java 文法、Java 集合架構、Java IO、Java 并發程式設計、Java 虛拟機等核心知識點。

亮白版和暗黑版的 PDF 也準備好了呢,一起成為更好的 Java 工程師,沖!