使用Stream需要三个操作步骤:创建Stream、中间操作以及结束操作
。
注意:因为stream操作是延迟的,这就意味着它们会等到需要结果的时候才执行,只有有终止操作,流的相关操作才会执行。
stream并不是某种数据结构,它只是数据源的一种视图。这里的数据源可以是一个数组,Java容器或I/O channel等。正因如此要得到一个stream通常不会手动创建,而是调用对应的工具方法。
1、 调用Collection
系列集合的串行流:stream()
方法或者并行流: parallelStream()
方法
List<String> list = Arrays.asList("tom", "James");
// 获取并行的Stream对象
Stream<String> stream = list.parallelStream();
// 获取串行的Stream对象
Stream<String> stream = list.stream();
2、调用Arrays.stream(T[] array)
方法获取数组流
String[] array = {"aa", "bb", "cc"};
Stream<String> stream = Arrays.stream(array);
3、调用Stream.of()
方法
Stream<String> stream = Stream.of("tom", "james");
4、其他方式
Stream.iterate()
Stream.generate()
Stream对象提供的方法分成两类:
方法 | 说明 |
---|---|
sequential | 返回一个相等的串行的Stream对象,如果原Stream对象已经是串行就可能会返回原对象 |
parallel | 返回一个相等的并行的Stream对象,如果原Stream对象已经是并行的就会返回原对象 |
unordered | 返回一个不关心顺序的Stream对象,如果原对象已经是这类型的对象就会返回原对象 |
onClose | 返回一个相等的Steam对象,同时新的Stream对象在执行Close方法时会调用传入的Runnable对象 |
close | 关闭Stream对象 |
filter | 元素过滤:对Stream对象按指定的Predicate进行过滤,返回的Stream对象中仅包含未被过滤的元素 |
map | 元素一对一转换:使用传入的Function对象对Stream中的所有元素进行处理,返回的Stream对象中的元素为原元素处理后的结果 |
mapToInt | 元素一对一转换:将原Stream中的使用传入的IntFunction加工后返回一个IntStream对象 |
flatMap | 元素一对多转换:对原Stream中的所有元素进行操作,每个元素会有一个或者多个结果,然后将返回的所有元素组合成一个统一的Stream并返回; |
distinct | 去重:返回一个去重后的Stream对象 |
sorted | 排序:返回排序后的Stream对象 |
peek | 使用传入的Consumer对象对所有元素进行消费后,返回一个新的包含所有原来元素的Stream对象 |
limit | 获取有限个元素组成新的Stream对象返回 |
skip | 抛弃前指定个元素后使用剩下的元素组成新的Stream返回 |
takeWhile | 如果Stream是有序的(Ordered),那么返回最长命中序列(符合传入的Predicate的最长命中序列)组成的Stream;如果是无序的,那么返回的是所有符合传入的Predicate的元素序列组成的Stream。 |
dropWhile | 与takeWhile相反,如果是有序的,返回除最长命中序列外的所有元素组成的Stream;如果是无序的,返回所有未命中的元素组成的Stream。 |
区分中间操作和结束操作最简单的方法,就是看方法的返回值,返回值为stream的大都是中间操作,否则是结束操作。
用于对Stream中的元素进行过滤,作用是返回一个只包含满足predicate
条件元素的Stream。
函数原型:
Stream<T> filter(Predicate<? super T> predicate);
使用示例:
Stream<String> stream = Stream.of("I", "love", "you", "too");
// 保留长度等于3的字符串
stream = stream.filter(str -> str.length()==3);
由于filter()是个中间操作,如果只调用filter()不会有实际计算,因此也不会输出任何信息。
作用是返回一个去除重复元素之后的Stream。
函数原型:
Stream<T> distinct();
使用示例:
Stream<String> stream = Stream.of("I", "you", "too", "too");
// 去掉一个too字符串
stream = stream.distinct();
返回的是排序好后的Stream。
如果不指定一个自定义的Comparator则会使用默认排序。
@Test
public void test() {
List<String> list = Arrays.asList("June", "Kmde", "Kang", "Zhan", "Gui");
Stream<String> stream = list.stream();
stream.sorted().forEach(System.out::println);
}
元素一对一转换。
它接收一个Funcation
参数,用其对Stream
中的所有元素进行处理,返回的Stream
对象中的元素为Function
对原元素处理后的结果 。
直观的说,就是对每个元素按照某种操作进行转换,转换前后Stream
中元素的个数不会改变。
函数原型:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
使用示例:
Stream<String> stream = Stream.of("I", "love", "you", "too");
// 将所有字符串转化成大写
stream = stream.map(str -> str.toUpperCase());
stream.forEach(System.out::println);
结果:
I
LOVE
YOU
TOO
从结果看出,流中的每个元素都应用了map()里的参数中的Function函数,并返回经过Function处理的元素。
元素一对多转换。
它对原Stream
中的所有元素使用传入的Function
进行处理,每个元素经过处理后生成一个多个元素的Stream
对象,然后将返回的所有Stream
对象中的所有元素组合成一个统一的Stream
并返回。转换前后元素的个数和类型都可能会改变。
函数原型:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
使用示例:
Stream<List<Integer>> stream = Stream.of(Arrays.asList(1,2), Arrays.asList(3, 4, 5));
// 原来的stream中有两个元素,分别是两个List,执行flatMap()之后,将每个List都“摊平”成了一个个的数字,所以会新产生一个由5个数字组成的Stream
stream = stream.flatMap(list -> list.stream());
Stream<String> stream = Stream.of("tom", "James");
// 将每一个String元素拆分成单个字母的Stream
stream = stream.flatMap(n -> Stream.of(n.split("")));
map、flatMap区别:
方法 | 说明 |
---|---|
iterator | 返回Stream中所有对象的迭代器; |
spliterator | 返回对所有对象进行的spliterator对象 |
forEach | 对所有元素进行迭代处理,无返回值 |
forEachOrdered | 按Stream的Encounter所决定的序列进行迭代处理,无返回值 |
toArray | 返回所有元素的数组 |
reduce | 使用一个初始化的值,与Stream中的元素一一做传入的二合运算后返回最终的值。每与一个元素做运算后的结果,再与下一个元素做运算。它不保证会按序列执行整个过程。 |
collect | 根据传入参数做相关汇聚计算 |
min | 返回所有元素中最小值的Optional对象;如果Stream中无任何元素,那么返回的Optional对象为Empty |
max | 与Min相反 |
count | 所有元素个数 |
anyMatch | 只要其中有一个元素满足传入的Predicate时返回True,否则返回False |
allMatch | 所有元素均满足传入的Predicate时返回True,否则False |
noneMatch | 所有元素均不满足传入的Predicate时返回True,否则False |
findFirst | 返回第一个元素的Optioanl对象;如果无元素返回的是空的Optional; 如果Stream是无序的,那么任何元素都可能被返回。 |
findAny | 返回任意一个元素的Optional对象,如果无元素返回的是空的Optioanl。 |
isParallel | 判断是否当前Stream对象是并行的 |
作用是对容器中的每个元素执行action指定的动作,也就是对元素进行遍历。
函数原型:
void forEach(Consumer<? super E> action)
使用示例:
// 使用Stream.forEach()迭代
Stream<String> stream = Stream.of("I", "love", "you", "too");
stream.forEach(System.out::println);
运行结果:
由于forEach()是结束方法,上述代码会立即执行,输出所有字符串。
规约操作(reduction operation)又被称作折叠操作(fold),是通过某个连接动作将所有元素汇总成一个汇总结果的过程。元素求和、求最大值或最小值、求出元素总个数、将所有元素转换成一个列表或集合,都属于规约操作。
Stream类库有两个通用的规约操作reduce()
和collect()
,也有一些为简化书写而设计的专用规约操作,比如sum()、max()、min()、count()
等。
最大或最小值这类规约操作很好理解(至少方法语义上是这样),我们着重介绍reduce()
和collect()
,这是比较有魔法的地方。
使用示例:
@Test
public void test() {
List<Integer> list = Arrays.asList(1, 2, 3, 4);
// x:上一次执行的返回值,默认0;y:当前遍历的值
Integer sum = list.stream().reduce(0, (x, y) -> {
System.out.println("上次的返回值:" + x);
System.out.println("这一次遍历的值:" + y);
return x + y;
});
System.out.println("总和:" + sum);
System.out.println("---黄金分割线---");
List<String> list2 = Arrays.asList("June", "Kmde", "Kang", "Zhan", "Gui");
Optional<String> result = list2.stream().reduce((x, y) -> x + "_" + y);
System.out.println(result.get());
}
接收的参数是将流中的元素累积到汇总结果的各种方式(称为收集器)。
可以转化成List、Set、Map …
// 把流中所有元素收集到List中
List<String> names = stream1.map(Student::getName).collect(Collectors.toList());
// 把流中所有元素收集到Set中,删除重复项
Set<String> names = stream1.map(Student::getName).collect(Collectors.toSet());
// 把流中所有元素收集到给定的供应源创建的集合中
Set<String> names = stream1.map(Student::getName).collect(Collectors.toCollection(HashSet::new));
// 把流中所有元素收集到Map中
Map<Integer, String> names = stream1.distinct().collect(Collectors.toMap(Student::getId, Student::getName));
//将集合中所有的名字连接在一起,并逗号分割
String allNameStr1 = stuList.stream().map(Stu::getName).collect(Collectors.joining(","));
参考:
https://segmentfault.com/a/1190000019143092
Java8新特性学习-Stream的Reduce及Collect方法详解