JDK1.8中的stream的操作总结

前言

Stream是Java8的一大亮点,是对容器对象功能的增强,专注于对容器对象进行各种非常便利、高效的 聚合操作(aggregate operation)或者大批量数据操作。Stream API借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性。同时,它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。所以说,Java8中首次出现的 java.util.stream是一个函数式语言+多核时代综合影响的产物。
  接下来我们就通过归纳总结来回顾一下jdk8中stream的一些函数以及stream的一些特性,加深对stream的理解和使用。

筛选/过滤(filter)

日常中对于数据的筛选是非常普通的操作了,以前通常都会放在数据库筛选或者自己写方法进行筛选。
获取某些行数据,一行就搞定。

List<SysUser> users = mapper.getAllUsers();
// 获取指定id的用户
List<SysUser> targetUsers = users.stream().filter(u->ids.contains(u.getId())).collect(Collectors.toList());

// .collect(Collectors.toList()):可以根据需要换成其他集合

属性处理(map)

该操作的作用就是将input Stream的每一个元素,按照一定规则处理之后,映射成output Stream的另一个元素,相当于一对一的输入输出,平时该方式使用较多。

比如说:

List<SysUser> users = mapper.getAllUsers();
// 把名字转换成大写;
List<SysUser> targetUsers = users.stream().map(u->u.getName().toUpperCase()).collect(Collectors.toList());

mapToInt/mapToLong/mapToDouble方法

顾名思义,用于转换流类型的方法。这个没有什么可说的。

flatMap方法

这个操作方法被称作:平铺,我理解的更像是集合的合并操作。看下面的例子。

Stream<List<Integer>> inputStream = Stream.of(
        Arrays.asList(1),
        Arrays.asList(2, 3),
        Arrays.asList(4, 5, 6)
);
Stream<Integer> outputStream = inputStream.flatMap((childList) -> childList.stream());
List<Integer> list = outputStream.collect(Collectors.toList());
System.out.println(list);
// [1,2,3,4,5,6]

将二维转成了一维。

foreach遍历

比较简单,可能需要注意的是,不能修改自己包含的本地变量值,也不能使用 break/return 之类的关键字提前结束循环。

findFirst方法

获取第一个元素。但没有获取第二个、第三个、最后一个元素的函数方法。

reduce方法

这个方法的作用主要是把Stream中的元素组合起来,比如说字符串拼接,数值类型的求和等都是特殊的reduce操作,并且我们可以根据重载方法选择是否有初始值。

// 字符串连接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
// 求和的另一种形式
int sum = Stream.of(1, 2, 3, 4).reduce(0, (a,b) -> a+b);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
// 过滤,字符串连接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F")
        .filter(x -> x.compareTo("Z") > 0)
        .reduce("", String::concat);

reduce方法可以结合filter,map进行各种复合操作,是我们非常常用的方法。也是我们需要掌握和理解的方法。

limit/skip方法

limit是取流前n个元素,skip是跳过前m个元素剩余的元素,这两个方法有点像用来做分页用的。在mongodb操作内存数据时经常会使用到这些方法。

sorted方法

进行排序,默认的自然排序规则进行排序, 也可以指定具体的比较器来进行排序。

List<Integer> list = Arrays.asList(5, 7, 1, 4, 2, 6, 3, 8, 9, 10);
list.stream().limit(5).sorted().forEach(System.out::print);
System.out.println();
list.stream().limit(5).sorted(Comparator.reverseOrder()).forEach(System.out::print);

min/max/distinct方法

Stream中的findAny、max/min、reduce等方法等返回Optional值。还有例如IntStream.average()返回OptionalDouble等等.这里比较重点的是Optional的返回值类型,Optional:这也是一个模仿 Scala 语言中的概念,作为一个容器,它可能含有某值,或者不包含,使用它的目的是尽可能避免NullPointerException。

// Optional 的两个用例

 // Java 8
 Optional.ofNullable(message).ifPresent(System.out::println);

 // Pre-Java 8
 if (message != null) {
 System.out.println(message);
 }

 // Java 8
return Optional.ofNullable(message).map(String::length).orElse(-1);

// Pre-Java 8
return if (message != null) ? message.length() : -1;
 };

使用Optional代码的可读性更好,而且它提供的是编译时检查,能极大的降低NPE这种Runtime Exception 对程序的影响,或者迫使程序员更早的在编码阶段处理空值问题,而不是留到运行时再发现和调试。

小结

Stream 的特性可以归纳为:

1.不是数据结构;
2.它不做内部存储,它只是用操作管道从source(数据结构、数组、generator function、IO channel)抓取数据;
3.它也绝不修改自己所封装的底层数据结构的数据。例如Stream的filter操作会产生一个不包含被过滤元素的新Stream,而不是从source删除那些元素;
4.所有Stream的操作必须以lambda表达式为参数;不支持索引访问;
5.你可以请求第一个元素,但无法请求第二个,第三个,或最后一个;
6.惰性化:到具体使用才会进行操作;很多Stream操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始;
7.Intermediate操作永远是惰性化的;
8.当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的;
9.可以是无限的。集合有固定大小,Stream 则不必。limit(n)和findFirst()这类的short-circuiting操作可以对无限的Stream进行运算并很快完成。

猜你喜欢

转载自blog.51cto.com/4837471/2466399