集合类关系:
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
可见,并行运算无法保证每个元素被处理的顺序。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。