Java容器類研究1:Collection

集合類關係:java

Collection   
├List   
│├LinkedList   
│├ArrayList   
│└Vector   
│ └Stack   
└Set   
Map  
├Hashtable  
├HashMap   
└WeakHashMap

<!--more-->安全

Collection

java.util.Collection 函數

Collection是List和Set的父接口。它繼承了Iterable接口,因此每一個Collection的子類應該是能夠迭代訪問其中的元素的。ui

我注意到一個有意思的函數,該函數在Java1.8中引入。該函數的功能是從集合中刪除全部知足條件的元素,代碼實現平平無奇,主要是函數有一個default修飾。Java8提供了default讓接口中也能夠實現方法體,目的是爲了讓開發者在給interface添加新方法時,沒必要再一一修改實現該接口的類,這些類可使用默認的方法實現。線程

default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

Collection的equals

Collection的equals方法的重寫須要當心謹慎。簡單的使用引用比較仍是比較簡單安全的,值比較則會變複雜。相等必須是對稱的,約定List只能和其它List相等,Set亦然。因此你本身實現的Collection類在和List、Set比較時應該返回false,由於即便你定製的Collection能夠返回true,可是從List的視角來比較,返回的是false,不知足對稱性。所以,也沒法正確的實現一個既有List接口,又有Set接口的類。code

遵守約定,若是你重寫了equals方法,那麼你要同時重寫hashCode方法。c1.equals(c2)成立,則c1.hashCode()==c2.hashCode()。繼承

Spliterator和Stream

Spliterator接口在Java8中引入,這個單詞是Split和iterator的合成,用來分割集合以給並行處理提供方便。看個例子:遞歸

public class Ripper {
    public static void main(String[] args) {
        Collection<Integer> numbers = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            numbers.add(i);
        }

        Spliterator<Integer> sp = numbers.spliterator();
        System.out.println(sp.characteristics()); 
        System.out.println(sp.estimateSize());

        Spliterator<Integer> sp2=sp.trySplit();
        System.out.println(sp.estimateSize());
        System.out.println(sp2.estimateSize());

        Spliterator<Integer> sp3=sp.trySplit();
        System.out.println(sp.estimateSize());
        System.out.println(sp3.estimateSize());

        sp3.
    }
}

運行結果:接口

16464
10
5
5
3
2

相較於傳統的iterator,spliterator能夠遞歸的對集合進行劃分,每一個spliterator管理了原來集合中的部分元素。可是,每一個spliterator並非線程安全的,因此並行處理時,要保證每個劃分在同一個線程中進行處理。ip

Collection提供Stream對元素進行流處理,其中用到了spliterator。看個例子:

public class Ripper {
    public static void main(String[] args) {
        Collection<Integer> numbers = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            numbers.add(i);
        }

        Stream<Integer> stream = numbers.stream();
        List<Integer> filterNum=stream.filter(item -> item > 5).collect(Collectors.toList());
        for(Integer i:filterNum){
            System.out.print(i+" ");
        }
        filterNum.set(0,100);
        System.out.println();
        for(Integer i:numbers){
            System.out.print(i+" ");
        }
    }
}

結果:

6 7 8 9 
0 1 2 3 4 5 6 7 8 9

集合通過兩步處理,過濾出了全部符合條件的元素。Stream總體處理過程分爲兩步:1.Configuration,2.Processing。Filter是Configuration,collect是Processing。還能夠看出一點,最後獲取的結果List是一個新建的List,並不和原List共享內存中的元素。

再看一個reduce的例子:

public class Ripper {
    public static void main(String[] args) {
        Collection<Integer> numbers = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            numbers.add(i);
        }

        Stream<Integer> stream = numbers.stream();
        int result=stream.reduce(0, (acc, item) -> acc + item);
        System.out.println(result);
    }
}

結果是:45。這是一個求和運算,其中第一個參數0是acc的初始值,acc表示上一步(acc, item) -> acc + item的結果,item是每次從stream中取的值。這些Configuration並不當即執行,而是等到最後一個Processing函數,統一執行。

在Collection中有parallelStream提供並行運算,而且使用了默認的spliterator對集合進行劃分。例子以下:

public class Ripper {
    public static void main(String[] args) {
        Collection<Integer> numbers = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            numbers.add(i);
        }

        Stream<Integer> stream = numbers.parallelStream();
        stream.forEach(item -> System.out.print(item+" "));
        System.out.println();
        stream=numbers.stream();
        stream.forEach(item -> System.out.print(item+" "));
    }
}

結果:

1 2 6 8 0 4 3 5 9 7 
0 1 2 3 4 5 6 7 8 9

可見,並行運算沒法保證每一個元素被處理的順序。

相關文章
相關標籤/搜索