为什么需要Stream
Java 8中的Stream是对集合(Collection)对象功能的增强,专注于对集合对象进行便利、高效的聚合操作,或者大批量数据操作。Stream API 使用Java 8的新语法Lambda表达式,极大提高编程效率和代码可读性。同时提供串行和并行两种模式,并行操作可以充分利用现代多核处理器的优势,封装多核并行编程的复杂性,对程序员编程更友善。Java 8中的流式编程是函数式语言+多核时代的产物。
Java的并行API演变历程
- 1.0~1.4中的java.lang.Thread
- 5.0中的java.util.concurrent
- 6.0中Phasers等
- 7.0中的Fork/Join框架
- 8.0中的Lambda
最近看了《七周七语言》一书,觉得Java8的流式编程和Lambda表达式,借鉴了许多其他编程语言的特性,特别是基于JVM上的脚本语言。
流的构成
- Collection和数组
Collection.stream()
Collection.parallelStream()
Arrays.stream(T array)
Stream.of()
- BufferReader
java.io.BufferedReader.lines()
- 静态工厂
java.util.stream.IntStream.range()
java.nio.file.Files.walk()
- 自己构造
java.util.Spliterator
- 其他
Random.ints()
BitSet.stream()
Pattern.splitAsStream(java.lang.CharSequence)
JarFile.stream()
流的操作类型
- Intermediate
类似于shell命令的管道符,后续可以接多个命令。这类操作是惰性化的(lazy),只是调用方法,并没有真正开始流的遍历。
map(mapToInt,flatMap等)、filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered
特别注意flatMap的使用
- Terminal
一个流只能进行一次terminal操作,即 流只能被消费一次,terminal操作才会开始真正流的遍历。因此多次Intermediate操作不会增加时间复杂度。
forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator
- short-circuiting
对于一个intermediate操作,如果接受的是一个无限大的Stream,但返回一个有限的新Stream。
对于一个terminal操作,如果接受的是一个无限大的Stream,但能在有限的时间内计算出结果(side effect)。
当操作一个无限大的流,又希望在有限时间内完成操作,则在管道内拥有一个short-circuriting操作是必要非充分条件(如allMatch,全都满足条件😭)
anyMatch、allMatch、noneMatch、findFirst、findAny、limit
生成自己的流
- Stream.generate
- Stream.iterate
Stream.iterate(0, n -> n + 3).limit(10).forEach(x -> System.out.print(x + " ");
- IntStream.range()
前开后闭
- IntStream.rangeClosed()
闭区间
IntStream LongStream DoubleStream
提供基本数据类型,防止boxing和unboxing影响性能
用Collectors来进行reduction操作
java.util.stream.Collectors
groupingBy/partitioningBy
partitioningBy是一种特殊的groupingBy,分成2组