集合类关系:

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

<!--more-->

Collection

java.util.Collection

Collection是List和Set的父接口。它继承了Iterable接口,所以每个Collection的子类应该是可以迭代访问其中的元素的。

我注意到一个有意思的函数,该函数在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接口的类。

遵照约定,如果你重写了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并不是线程安全的,所以并行处理时,要保证每一个划分在同一个线程中进行处理。

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 

可见,并行运算无法保证每个元素被处理的顺序。


天天吃
66 声望3 粉丝

善行,无辙迹;善言,无瑕谪;善计,不用筹策;善闭,无关楗而不可开;善结,无绳约而不可解。