概念:Steam 是Java8 提出的一个新概念,不是输入输出的 Stream 流,而是一种用函数式编程方式在集合类上进行复杂操作的工具。简而言之,是以内部迭代的方式处理集合数据的操作,内部迭代可以将更多的控制权交给集合类。Stream 和 Iterator 的功能类似,只是 Iterator 是以外部迭代的形式处理集合数据的操作。
在Java8以前,对集合的操作需要写出处理的过程,如在集合中筛选出满足条件的数据,需要一 一遍历集合中的每个元素,再把每个元素逐一判断是否满足条件,最后将满足条件的元素保存返回。而Stream 对集合筛选的操作提供了一种更为便捷的操作,只需将实现函数接口的筛选条件作为参数传递进来,Stream会自行操作并将合适的元素同样以stream 的方式返回,最后进行接收即可。
2种操作:
1.intermediate operation 中间操作:中间操作的结果是刻画、描述了一个Stream,并没有产生一个新集合,这种操作也叫做惰性求值方法。
2.terminal operation 终止操作:最终会从Stream中得到值。
如何区分这2种操作呢?可以根据操作的返回值类型判断,如果返回值是Stream,则该操作是中间操作,如果返回值是其他值或者为空,则该操作是终止操作。
如下图的前2个操作是中间操作,只有最后一个操作是终止操作。
可以形象地理解Stream的操作是对一组粗糙的工艺品原型(即对应的 Stream 数据源)进行加工成颜色统一的工艺品(即最终得到的结果),第一步筛选出合适的原型(即对应Stream的 filter 的方法),第二步将这些筛选出来的原型工艺品上色(对应Stream的map方法),第三步取下这些上好色的工艺品(即对应Stream的 collect(toList())方法)。在取下工艺品之前进行的操作都是中间操作,可以有多个或者0个中间操作,但每个Stream数据源只能有一次终止操作,否则程序会报错。
常用的 Stream 流操作:
1.collect(toList()) 终止操作
由Stream中的值生成一个List列表,也可用collect(toSet())生成一个Set集合。
例:取 Stream 中每个字符串并放入一个新的列表,
@Test
public void collectToList() {
String[] testStrings = { "java", "react", "angular", "vue" };
List<String> list = Stream.of(testStrings).collect(Collectors.toList());
for (int i = 0, length = list.size(); i < length; i++) {
System.out.println(list.get(i));
}
}
2.map 中间操作
将一种类型的值映射为另一种类型的值,可以将 Stream 中的每个值都映射为一个新的值,最终转换为一个新的 Stream 流。
例:把 Stream 中每个字符串都转换为大写的形式,
@Test
public void mapTest() {
String[] testStrings = { "java", "react", "angular", "vue" };
List<String> list = Stream.of(testStrings).map(test -> test.toUpperCase()).collect(Collectors.toList());
list.forEach(test -> System.out.println(test));
}
3.filter 中间操作
遍历并筛选出满足条件的元素形成一个新的 Stream 流。
例:筛选出以 j 字母开头的元素个数,此例中的count方法也是终止操作,是为了计算出 Stream 中的元素个数,
@Test
public void filterTest() {
List<String> list = Arrays.asList("java", "react", "angular", "javascript", "vue");
long count = list.stream().filter(p -> p.startsWith("j")).count();
System.out.println(count);
}
4.flatMap 中间操作
可用 Stream 替换值,并将多个 Stream 流合并成一个 Stream 流。
例:将含有一串数字的两个流合并为一个流,
@Test
public void flapMapTest() {
List<Integer> list = (List<Integer>) Stream.of(Arrays.asList(1, 2, 3, 4, 5, 6), Arrays.asList(8, 9, 10, 11, 12))
.flatMap(test -> test.stream()).collect(Collectors.toList());
for (int i = 0, length = list.size(); i < length; i++) {
System.out.println(list.get(i));
}
}
5.max 、min 终止操作
求 Stream 中的最大值、最小值。
例:取出 Stream 中最长的字符串
@Test
public void maxTest() {
String[] testStrings = { "java", "react", "angular", "javascript", "vue" };
Optional<String> max = Stream.of(testStrings).max((p1, p2) -> Integer.compare(p1.length(), p2.length()));
System.out.println(max);
}
6.reduce 终止操作
从 Stream 的一组值中生成另一个值。
例:上述的max、min、count 实际上都是 reduce 操作,求出 Stream 元素数值的总和,
@Test
public void reduceSumTest() {
int sum = Stream.of(5, 6, 7, 8).reduce(0, (accumulator, element) -> accumulator + element);
System.out.println(sum);
}
reduce方法的第一个参数值 0 是初始值,第二个lambda表达式参数 (accumulator, element) -> accumulator + element 是执行求和操作,其中 accumulator 是累加器,element 是每次迭代的当前元素数值。
注意:Stream 流操作远不止以上介绍的几种,这里只介绍流操作的认识和几个简单实现,更多的流操作可查阅 API 官方文档,灵活结合 lambda 表达式和 Stream 操作可以更愉快地完成常见的集合操作。