写点什么

Java- 技术专题 -Stream 流详解

发布于: 2020 年 10 月 30 日
Java-技术专题-Stream流详解

首先,Stream 流有一些特性:

  1. Stream 流不是一种数据结构,不保存数据,它只是在原数据集上定义了一组操作。

  2. 这些操作是惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。

  3. Stream 不保存数据,故每个 Stream 流只能使用一次。

关于应用在 Stream 流上的操作,可以分成两种:Intermediate(中间操作)和 Terminal(终止操作)。中间操作的返回结果都是 Stream,故可以多个中间操作叠加;终止操作用于返回我们最终需要的数据,只能有一个终止操作。至于哪些方法是中间操作,哪些方法是终止操作,我们一会儿再说

使用 Stream 流,可以清楚地知道我们要对一个数据集做何种操作,可读性强。而且可以很轻松地获取并行化 Stream 流,不用自己编写多线程代码,可以让我们更加专注于业务逻辑。

接下来看一下 Stream 流的接口继承关系:

基础接口 BaseStream<T, S extends BaseStream<T, S>>包含如下方法:

Iterator<T> iterator();Spliterator<T> spliterator();boolean isParallel(); //判断是否是并行化流S sequential(); //将流串行化S parallel(); //将流并行化S unordered();  //解除有序流的顺序限制,发挥并行处理的性能优势S onClose(Runnable closeHandler);void close();
复制代码

默认情况下,从有序集合、生成器、迭代器产生的流或者通过调用 Stream.sorted 产生的流都是有序流,有序流在并行处理时会在处理完成之后恢复原顺序。

unordered()方法可以解除有序流的顺序限制,更好地发挥并行处理的性能优势,例如 distinct 将保存任意一个唯一元素而不是第一个,limit 将保留任意 n 个元素而不是前 n 个。

BaseStream 的四个子接口方法都差不多,只是 IntStream、LongStream、DoubleStream 直接存储基本类型,可以避免自动装/拆箱,效率会更高一些。下面以 Stream 为例,将接口的方法分类讲解一下。

一、 流的生成方法

  1. Collection 接口的 stream()或 parallelStream()方法

  2. 静态的 Stream.of()、Stream.empty()方法

  3. Arrays.stream(array, from, to)

  4. 静态的 Stream.generate()方法生成无限流,接受一个不包含引元的函数

  5. 静态的 Stream.iterate()方法生成无限流,接受一个种子值以及一个迭代函数

  6. Pattern 接口的 splitAsStream(input)方法

  7. 静态的 Files.lines(path)、Files.lines(path, charSet)方法

  8. 静态的 Stream.concat()方法将两个流连接起来

注意,无限流的存在,侧面说明了流是惰性的,即每当用到一个元素时,才会在这个元素上执行这一系列操作。

二、 流的 Intermediate 方法(中间操作)

  • filter(Predicate) 将结果为 false 的元素过滤掉

  • map(fun) 转换元素的值,可以用方法引元或者 lambda 表达式

  • flatMap(fun) 若元素是流,将流摊平为正常元素,再进行元素转换

  • limit(n) 保留前 n 个元素

  • skip(n) 跳过前 n 个元素

  • distinct() 剔除重复元素

  • sorted() 将 Comparable 元素的流排序

  • sorted(Comparator) 将流元素按 Comparator 排序

  • peek(fun) 流不变,但会把每个元素传入 fun 执行,可以用作调试

三、流的 Terminal 方法(终结操作)

约简操作

  • max(Comparator)

  • min(Comparator)

  • count()

  • findFirst():返回第一个元素

  • findAny():返回任意元素

  • anyMatch(Predicate):任意元素匹配时返回 true

  • allMatch(Predicate):所有元素匹配时返回 true

  • noneMatch(Predicate):没有元素匹配时返回 true

  • reduce(fun):从流中计算某个值,接受一个二元函数作为累积器,从前两个元素开始持续应用它,累积器的中间结果作为第一个参数,流元素作为第二个参数

  • reduce(a, fun):a 为幺元值,作为累积器的起点

  • reduce(a, fun1, fun2):与二元变形类似,并发操作中,当累积器的第一个参数与第二个参数都为流元素类型时,可以对各个中间结果也应用累积器进行合并,但是当累积器的第一个参数不是流元素类型而是类型 T 的时候,各个中间结果也为类型 T,需要 fun2 来将各个中间结果进行合并

收集操作

  • iterator()

  • forEach(fun)

  • forEachOrdered(fun):可以应用在并行流上以保持元素顺序

  • toArray()

  • toArray(T[] :: new):返回正确的元素类型

  • collect(Collector)

  • collect(fun1, fun2, fun3):fun1 转换流元素;fun2 为累积器,将 fun1 的转换结果累积起来;fun3 为组合器,将并行处理过程中累积器的各个结果组合起来

Collector 收集器:

  • Collectors.toList()

  • Collectors.toSet()

  • Collectors.toCollection(集合的构造器引用)

  • Collectors.joining()、Collectors.joining(delimiter)、Collectors.joining(delimiter、prefix、suffix)

字符串元素连接

Collectors.summarizingInt/Long/Double(ToInt/Long/DoubleFunction)

产生 Int/Long/DoubleSummaryStatistics 对象,它有 getCount、getSum、getMax、getMin 方法,注意在没有元素时,getMax 和 getMin 返回 Integer/Long/Double.MAX/MIN_VALUE

Collectors.toMap(fun1, fun2)/toConcurrentMap

两个 fun 用来产生键和值,若值为元素本身,则 fun2 为 Function.identity()

Collectors.toMap(fun1, fun2, fun3)/toConcurrentMap

fun3 用于解决键冲突,例如(oldValue, newValue) -> oldValue,有冲突时保留原值

Collectors.toMap(fun1, fun2, fun3, fun4)/toConcurrentMap

默认返回 HashMap 或 ConcurrentHashMap,fun4 可以指定返回的 Map 类型,为对应的构造器引元

Collectors.groupingBy(fun)/groupingByConcurrent(fun)

fun 是分类函数,生成 Map,键是 fun 函数结果,值是具有相同 fun 函数结果元素的列表

Collectors.partitioningBy(fun)

键是 true/false,当 fun 是断言函数时用此方法,比 groupingBy(fun)更高效

Collectors.groupingBy(fun1, fun2)

fun2 为下游收集器,可以将列表转换成其他形式,例如 toSet()、counting()、summingInt/Long/Double(fun)、maxBy(Comparator)、minBy(Comparator)、mapping(fun1, fun2)(fun1 为转换函数,fun2 为下游收集器)

最后提一下基本类型流,与对象流的不同点如下:

  1. IntStream 和 LongStream 有 range(start, end)和 rangeClosed(start, end)方法,可以生成步长为 1 的整数范围,前者不包括 end,后者包括 end

  2. toArray 方法将返回基本类型数组

  3. 具有 sum、average、max、min 方法

  4. summaryStatics()方法会产生类型为 Int/Long/DoubleSummaryStatistics 的对象

  5. 可以使用 Random 类的 ints、longs、doubles 方法产生随机数构成的流

  6. 对象流转换为基本类型流:mapToInt()、mapToLong()、mapToDouble()

  7. 基本类型流转换为对象流:boxed()


用户头像

我们始于迷惘,终于更高的迷惘。 2020.03.25 加入

一个酷爱计算机技术、健身运动、悬疑推理的极客狂人,大力推荐安利Java官方文档:https://docs.oracle.com/javase/specs/index.html

评论

发布
暂无评论
Java-技术专题-Stream流详解