Java流库(下篇——reduce、基本类型流、并行流)

250 阅读3分钟

reduce

Stream.reduce是一个可以灵活的针对流中的数据进行计算的通用方法。这个方法在实际开发中可能用的不多,不过了解总比不知道的好。

用法1

对流中所有的元素求和

Stream<Integer> hello = Stream.of(2,3,4,4,2,3,5,6,4,4,7,8,8,8);
Optional<Integer> reduce = hello.reduce(Integer::sum);
System.out.println(reduce.get());

输出:68

用法2

提供一个初始值100,让流中的元素在其基础上累计求和

Stream<Integer> hello = Stream.of(2,3,4,4,2,3,5,6,4,4,7,8,8,8);
Integer sum = hello.reduce(100, Integer::sum);
System.out.println(sum);

输出:168

用法3
Stream<String> hello = cs();
Integer sum = hello.parallel().reduce(100, (total, word) -> total + word.length(), Integer::sum);
System.out.println(sum);

输出:420

上面这个示例中的reduce方法中有三个参数并且它的返回值是一个与第一个参数相同的类型。第一个参数表示一个初始值要与返回值类型相同;第二个参数是一个Function类型,它的第一个参数是就是前面提供的初始值,第二个参数是当前流的元素;第三个参数用来累积不同部分的值,这个参数在多线程中才有意义。上面每一个部分的值是105,第二个参数中,加数为100,被加数为5。第三个参数累加每一部分的值,由于Stream中4个字符串长度都为5所以结果=105 * 4 = 420

基本类型流

一般情况下,我们在使用流处理基本类型数据的时候,都是将这个数据给包装成它们各自的引用类型。比如int类型使用Integerdouble类型使用Double;就像这样Stream<Integer>实际上流库中有具体的处理基本数据类型的流类型,可以直接使用这些流类型直接操作基本数据类型,可以提高处理的效率。

IntStream
IntStream s = IntStream.of(1, 2, 34, 5, 6, 1, 6);
IntStream sum = s.map(n -> n++);
sum.forEach(System.out::print);

输出:12345616

LongStream
LongStream s = LongStream.of(1, 2, 34, 5, 6, 1, 6);
long sum = s.map(n -> n++).sum();
System.out.println(sum);

输出:55

DoubleStream
DoubleStream s = DoubleStream.of(1, 2, 34, 5, 6, 1, 6);
double sum = s.map(n -> n++).count();
System.out.println(sum);

输出:7.0

可以从示例代码中看出,基本类型流和对象流的使用是类似的。这里只需要记得处理int、short、byte、char、boolean类型用IntStream,处理longLongStream,处理floatdoubleDoubleStream

并行流

流的并行操作是自动的,并且默认的线程池是ForkJoinPool一种归并方式处理任务的线程池。流库中已经为开发者封装了获取并行流的方法,所以无需知道具体细节,直接调用方法即可。创建并行流的几种方式如下:

Collection.parallelStream()
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵四");
Stream<String> stream = list.parallelStream().peek((n)-> System.out.println(Thread.currentThread().getName()));
stream.forEach(System.out::println);

输出:
main
王五
ForkJoinPool.commonPool-worker-5
赵四
ForkJoinPool.commonPool-worker-3
李四
ForkJoinPool.commonPool-worker-7
张三

可以通过任意集合对象的parallelStream()获取一个并行流,并且从上面示例代码的运行结果中发现,并行流是任意顺序执行的

Stream.parallel()
//cs() = Stream.of("hello", "kitty", "hello", "kitty");
Stream<String> stream = cs().limit(3).parallel();
stream.forEach(System.out::println);

输出:
hello
hello
kitty

调用Stream对象的parallel()方法可以将一个流转为并行流。需要注意的是,只要在终止流时这个流变成了并行流,那么这个流中包含的所有操作都将是并行执行的。

关于并行流使用的注意事项
1.并行操作本身会耗费资源,所以如果数据量并不是很大,没有必要使用
2.只有任务可以被拆分成多个小任务时,才有必要使用并行流
3.当需要网络访问数据或通过文件读写数据时,有可能因为外部因素导致线程阻塞