Stream的基本认识
java和Stream相关的类都在java.util.stream包下。
java提供stream的目的:是为了更好的操作集合!
stream简单介绍
stream的基本构成:
1、源。
2、零个或者多个中间操作。
3、终止操作
stream的特性:
惰性求值、及早求值。
这句话怎么理解呢?stream中可能有多个中间操作,但是如果没有遇到终止操作,那么不管多少个中间操作都不会执行,只有遇到终止操作才会执行这些中间操作。
stream的创建方式
1、
Stream s = stream.of("hello","world","bins");
2、
String[] strings = new String[]{"hello","world"};
Stream stream = Arrays.stream(strings);
3、
List<String> list = Arrays.asList("hello","world","bins");
Stream stream = list.stream();
stream的特性
1、stream是不存储值的,通过管道的方式获取值
2、stream本质是函数式的,对stream操作会生成一个结果,但是并不会修改底层数据源。
3、同一个stream不能多次消费。
4、流虽然是通过集合来创建的。流关注的是集合中数据的计算,而集合关注的是数据和数据存储本身。
stream与迭代器的区别
1、stream可以并行化操作,迭代器只能命令式串行执行。
2、使用并行流去遍历的时候,数据会被分成多段,每段都会在不同的线程中执行,然后将结果合并输出。
3、stream并行流依赖于jdk7提供的fork/join框架。
外部迭代和内部迭代的区别
外部迭代操作的是集合,而内部迭代操作的是流。
注:
stream存在多个中间操作,但是这些中间操作并不会一个一个的迭代操作,而是通过stream中提供的一些特性值,将这些中间操作串联起来,遇到终止操作时一块执行!
stream存在短路操作。
比如下面这段代码:
List<String> list = Arrays.asList("hello","world","bins");
list.stream().mapToInt(item -> {
int lenth = item.length();
System.out.println(item);
return lenth;
}).filter(item -> item == 5).findFirst().ifPresent(System.out::println);
这个是通过findFirst来实现的,感觉和传统的for循环中的break类似。
List<String> list = Arrays.asList("hello","world","bins");
for(String s : list){
int lenth = s.length();
System.out.println(s);
if(lenth == 5){
System.out.println(s);
break;
}
}
map和flatMap使用
map和flatMap都是做映射的,但是如果想让多个stream同时操作就应该使用flatMap了,因为flatMap会将多个stream打平变成一个stream。
分组和分区
分组:通过Collectors.groupingBy()来实现分组,这个有很多重载方法。
分区:通过Collectors.partitionBy()来实现分区。partitioningBy也有重载方法。
收集器 Collector
Collector可以作为collect方法的参数,Collector是一个接口!支持串行、并行。
Collectors是一个类,它里面有很多方法可以返回Collector,并提供常见的汇聚实现。(类似于Collector的工厂类)
Collector有4个比较重要的方法
1、 Supplier supplier();创建一个结果容器,不接受参数,返回一个结果。
2、 BiConsumer<A, T> accumulator();将新的数据元素合并到结果容器,这个结果容器有supplier()方法提供!
3、BinaryOperator combiner();将两个结果容器合并到一个结果容器中!
4、Function<A, R> finisher();执行最终的转换,将中间类型转换成最终类型(这是一个可选操作!)
还有一个枚举类型的特性值 enum Characteristics
它包含3中类型
CONCURRENT 代表并行操作!
UNORDERED 代表无序的!
IDENTITY_FINISH 代表中间的结果容器和最终类型一致,可以进行强制类型转换,并不需要执行 finisher(),因此也证明了 finisher()是一个可选操作!
下面这段代码是自己构建一个Collector
package com.bins9.comparator;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public class MySetCollector2<T> implements Collector<T, Set<T>, Map<T,T>> {
@Override
public Supplier<Set<T>> supplier() {
System.out.println("supplier invoked!");
return () -> {
System.out.println("supplier 被使用");
return new HashSet<>();
};
}
@Override
public BiConsumer<Set<T>, T> accumulator() {
System.out.println("accumulator invoked!");
return (set,item) -> {
System.out.println("accumulator中执行线程名字::" + Thread.currentThread().getName() + "。set为:" + set + "。添加字段为:" + item);
set.add(item);
};
}
@Override
public BinaryOperator<Set<T>> combiner() {
System.out.println("combiner invoked!");
return (set1,set2) -> {
System.out.println("调用combiner啦————————————————");
set1.addAll(set2);
return set1;
};
}
@Override
public Function<Set<T>, Map<T, T>> finisher() {
System.out.println("finisher invoked!");
return set -> {
Map<T,T> map = new TreeMap<>();
set.forEach(item -> {
map.put(item,item);
});
return map;
};
}
@Override
public Set<Characteristics> characteristics() {
System.out.println("characteristics invoked");
System.out.println(Runtime.getRuntime().availableProcessors());
return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED));
}
public static void main(String[] args) {
for(int i = 0; i < 1; i ++){
List<String> list = Arrays.asList("hello","world","hello world","asd","qwe","qwedf","hello","z","x","c","j","p");
Set<String> set = new HashSet<>();
set.addAll(list);
System.out.println("set :" + set);
Map<String,String> map = set.stream().parallel().collect(new MySetCollector2<>());
System.out.println(map);
}
}
}
通过以上代码我们可以得到如下结论:
1、使用CONCURRENT特性时,结果容器只有一个,多个线程可以同时操作同一个结果容器。
2、如果使用并行流而不使用CONCURRENT,则会出现多个线程操作多个中间结果容器,则会使用combiner()方法,进行中间结果容器的合并,如果使用CONCURRENT,则只有一个结果容器,并不会执行combiner()方法!
注意:如果对顺序没有要求的话,可以使用groupingByConcurrent来进行排序!
使用groupingByConcurrent的前提是Characteristics的特性值必须为CONCURRENT、UNORDERED
还需要注意一下,groupingByConcurrent使用的中间结果容器是ConcurrentMap,线程安全的。
因为如果没有CONCURRENT,则并行流有几个线程就会创建几个中间结果容器,但是如果结果容器进行合并就会用到Synchronized关键字,保证线程安全,因此会造成一定性能的消耗。
但是如果有CONCURRENT,此时只有一个中间结果容器,并不需要conbiner方法,因此也就不会涉及到线程安全问题,ConcurrentMap本身就是线程安全的!
简单说一下比较器:
Comparator 这个也是一个函数式接口!
使用场景:Collections.sort(List list, Comparator<? super T> c),
例子:
Collections.sort(list, Comparator.comparingInt(String::length).thenComparing(item -> item));
如果第一次比较相等,可以使用thenComparing再次进行比较!