当你处理完流之后,你通常只是想查看一下结果,而不是将它们聚合为一个值。你可以调用 iterator 方法来生成一个传统风格的迭代器,用于访问元素。你也可以调用 toArray 方法获得含有流中所有元素的一个数组。
由于无法在运行时来创建一个泛型数组,所以表达式 stream.toArray() 会返回一个 Object[] 数组。如果你希望得到一个正确类型的数组,可以将类型传递给数组的构造函数。例如:
package com.hxstrive.jdk8.stream_api; import java.util.Arrays; import java.util.Iterator; import java.util.List; /** * 收集结果 * @author hxstrive.com */ public class StreamCollectDemo { public static void main(String[] args) { List<String> list = Arrays.asList("one", "two", "three"); Iterator<String> iterator = list.stream().map(String::toUpperCase).iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); } //输出: //ONE //TWO //THREE Object[] objects = list.stream().map(String::toUpperCase).toArray(); System.out.println(Arrays.toString(objects)); //输出:[ONE, TWO, THREE] String[] strings = list.stream().map(String::toUpperCase).toArray(String[]::new); System.out.println(Arrays.toString(strings)); //输出:[ONE, TWO, THREE] } }
现在假设你希望将结果收集到一个 HashSet 中。如果这个过程是并行的,那么你不能直接将元素放到一个单独的 Hashset 中,因为 HashSet 对象不是线程安全的,也正因此你不能使用聚合函数。并行收集中的每一段都需要从其所属的空 HashSet 开始,而聚合函数只能允许你提供一个标识值。因此,我们需要使用 collect 方法,它接受三个参数:
(1)一个能创建目标类型实例的方法,例如 HashSet 的构造函数。
(2)一个将元素添加到目标中的方法,例如一个 add 方法。
(3)一个将两个对象整合到一起的方法,例如 addAll 方法。
注意:目标对象不一定是集合。它可以是一个 StringBuilder 对象或者一个可以记录个数和总和的对象。
下面是如何使用 HashSet 的 collect 方法示例:
package com.hxstrive.jdk8.stream_api; import java.util.Arrays; import java.util.HashSet; import java.util.List; /** * 收集结果 * @author hxstrive.com */ public class StreamCollectDemo2 { public static void main(String[] args) { List<String> list = Arrays.asList("one", "two", "three"); // 使用 collect 收集结果 HashSet<String> hashSet = list.stream().map(String::toUpperCase) .collect(HashSet::new,HashSet::add, HashSet::addAll); System.out.println(hashSet); // [ONE, TWO, THREE] } }
下面将使用 collect 将结果收集到 StringBuffer 中:
List<String> list = Arrays.asList("one", "two", "three"); // 使用 collect 收集结果 StringBuffer buffer = list.stream().map(String::toUpperCase) .collect(StringBuffer::new, StringBuffer::append, StringBuffer::append); System.out.println(buffer); // ONETWOTHREE
在实际中,你不需要这么做,因为 Collector 接口已经为我们提供了这三个方法,并且 Collectors 类还为常用的收集类型提供了各个工厂方法。要将一个流收集到一个 list 或者 set 中,你只需要调用:
List<String> result = stream.collect(Collectors.toList());
或者
Set<String> result = stream.collect(Collectors.toSet());
如果你希望控制得到的 set 类型,可以使用如下方式的调用:
TreeSet<String> result = stream.collect(Collectors.toCollection(TreeSet::new));
假设你希望将流中的所有字符串连接并收集起来,你可以调用:
String result = stream.collect(Collectors.joining());
如果你希望在这些元素中间添加一个分隔符,那么就将分隔符传递给 joining 方法:
String result = stream.collect(Collectors.joining(","));
如果你的流包含字符串以外的对象,你需要首先将它们转换为字符串,如下所示:
String result = stream.map(Object::toString).collect(Collectors.joining(","));
如果你希望将流的结果聚合为一个总和、平均值、最大值或者最小值,那么请使用 summarizing(int|Long IDouble) 方法中的一种。这些方法会接受一个将流对象映射为一个数字的函数,并产生一个 (Int|LongIDouble) Summarystatistics 类型的结果,其中包含了获取总和、平均值、最大值和最小值的方法。例如:
package com.hxstrive.jdk8.stream_api; import java.util.Arrays; import java.util.IntSummaryStatistics; import java.util.List; import java.util.stream.Collectors; /** * 收集结果 * @author hxstrive.com */ public class StreamCollectDemo4 { public static void main(String[] args) { List<String> list = Arrays.asList("one", "two", "three"); IntSummaryStatistics summary = list.stream().collect(Collectors.summarizingInt(String::length)); System.out.println("count = " + summary.getCount()); System.out.println("avg = " + summary.getAverage()); System.out.println("max = " + summary.getMax()); System.out.println("min = " + summary.getMin()); System.out.println("sum = " + summary.getSum()); //输出: //count = 3 //avg = 3.6666666666666665 //max = 5 //min = 3 //sum = 11 } }
注意:到目前为止,你已经了解了如何聚合或收集流的值。但是也许你只需要将它们打印出来,或者将它们存到一个数据库中,那么你可以使用 forEach 方法,如下。
stream.forEach(System.out::println);
你向该方法传递的函数会被应用到流中的每个元素上。在一个并行流上,确保该函数是线程安全的,可以被并发执行是你的职责。
在一个并行流上,可能会以任意顺序来访问元素。如果你希望按照流的顺序来执行它们,那么请调用 forEachordered 方法。当然,这样做你可能会放弃大多数并行计算所能带来的好处。
forEach 和 forEachordered 方法都是终止操作。因此在调用它们之后,你就不能再使用这个流了。如果你希望还能继续使用这个流,请使用 peek 方法。例如:
package com.hxstrive.jdk8.stream_api; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * 收集结果 * @author hxstrive.com */ public class StreamCollectDemo5 { public static void main(String[] args) { List<String> list = Arrays.asList("one", "two", "three"); // peek 操作后,流还可继续操作 List<Integer> lengths = new ArrayList<>(); String[] strings = list.stream().peek(s -> { lengths.add(s.length()); }).map(String::toUpperCase).toArray(String[]::new); System.out.println(Arrays.toString(lengths.toArray())); System.out.println(Arrays.toString(strings)); //输出: //[3, 3, 5] //[ONE, TWO, THREE] //同步 list.forEach(s -> { System.out.println(Thread.currentThread().getName() + " = " + s); }); //输出: //main = one //main = two //main = three //异步 list.parallelStream().forEach(s -> { System.out.println(Thread.currentThread().getName() + " = " + s); }); //输出: //main = two //main = three //ForkJoinPool.commonPool-worker-1 = one list.parallelStream().forEachOrdered(s -> { System.out.println(Thread.currentThread().getName() + " = " + s); }); //输出: //ForkJoinPool.commonPool-worker-1 = one //ForkJoinPool.commonPool-worker-1 = two //ForkJoinPool.commonPool-worker-1 = three } }