当你处理完流之后,你通常只是想查看一下结果,而不是将它们聚合为一个值。你可以调用 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
}
}