java stream 原理,JAVA Stream简单介绍

Stream库

Java SE 8 中引入了这样一个库: java.util.stream 包,它有助于为各种数据来源的批量操作建立简明的、声明性的表达式。它是一个精心设计的库,充分利用了Lambda特性,可以将 Streams 视为 Java 中第一个充分利用了Lambda表达式的强大功能的库。

清单1:原来计算一个列表中除去1的总和的代码:

List list = Arrays.asList(1,2,3,4,5);

int sum = 0;

for(Integer i: list){

if(i == 1){

continue;

}

sum+=i;

}

清单2:使用Stream进行计算的代码:

List list = Arrays.asList(1,2,3,4,5);

int sum=list.stream().filter(t->t!=1).mapToInt(t->t).sum();

可以发现,使用Stream进行计算的代码更加简洁,并且可读性更强

流的关注点不是数据,而是计算,流具有很强的数据聚合、汇总能力。一个流管道包含一个流来源、0 或多个中间操作,以及一个终止操作。

使用流进行数据计算可以大致分为三个步骤:

1、由数据源构建流管道,即创建一个Stream

2、执行中间流操作

3、执行终止流操作

中间流操作负责将一个流转换为另一个流,中间流操作是惰性的:它只会设置流管道的下一个阶段,并不会启动任何操作。对数据的处理在执行终止操作时开始,执行终止操作之后,会终止当前流管道,该流管道不能再被使用,如果需要遍历同一个数据集,需要重新设置一个流管道。

中间操作可进一步划分为无状态(filter()、map()、flatMap())和有状态(sorted()、limit()、distinct())操作。无状态操作是可在元素上直接执行而无需知道其他任何元素的操作。例如,过滤操作只需检查当前元素来确定是包含还是消除它;有状态操作则表现为对当前元素的操作会受到其他元素的影响,如排序操作必须查看所有元素之后才能确定当前元素的位置顺序

构建流管道:

Collection.stream() 使用一个集合的元素创建一个流。

Stream.of(T...) 使用传递给工厂方法的参数创建一个流。

Stream.of(T[]) 使用一个数组的元素创建一个流。

Stream.empty() 创建一个空流。

Stream.iterate(T first, BinaryOperator f) 创建一个包含序列 first, f(first), f(f(first)), ... 的无限流

Stream.iterate(T first, Predicate test, BinaryOperator f) (仅限 Java 9)类似于 Stream.iterate(T first, BinaryOperator f),但流在测试预期返回 false 的第一个元素上终止。

Stream.generate(Supplier f) 使用一个生成器函数创建一个无限流。

IntStream.range(lower, upper) 创建一个由下限到上限(不含)之间的元素组成的 IntStream。

IntStream.rangeClosed(lower, upper) 创建一个由下限到上限(含)之间的元素组成的 IntStream。

BufferedReader.lines() 创建一个有来自 BufferedReader 的行组成的流。

BitSet.stream() 创建一个由 BitSet 中的设置位的索引组成的 IntStream。

Stream.chars() 创建一个与 String 中的字符对应的 IntStream。

中间流操作:

filter(Predicate) 与预期匹配的流的元素

map(Function) 将提供的函数应用于流的元素的结果

flatMap(Function> 将提供的流处理函数应用于流元素后获得的流元素

distinct() 已删除了重复的流元素

sorted() 按自然顺序排序的流元素

Sorted(Comparator) 按提供的比较符排序的流元素

limit(long) 截断至所提供长度的流元素

skip(long) 丢弃了前 N 个元素的流元素

takeWhile(Predicate) (仅限 Java 9)在第一个提供的预期不是 true 的元素处阶段的流元素

dropWhile(Predicate) (仅限 Java 9)丢弃了所提供的预期为 true 的初始元素分段的流元素

清单3:返回年龄小于22岁且身高由低到高排名在11-20名的同学的名字

List names=list.stream().sorted(Comparator.comparing(t->t.getHeight())).filter(t->t.getAge()< 22).map(t->t.getName()).skip(10).limit(10).collect(Collectors.toList());

终止流操作:

forEach(Consumer action) 将提供的操作应用于流的每个元素。

toArray() 使用流的元素创建一个数组。

reduce(...) 将流的元素聚合为一个汇总值。

collect(...) 将流的元素聚合到一个汇总结果容器中。

min(Comparator) 通过比较符返回流的最小元素。

max(Comparator) 通过比较符返回流的最大元素。

count() 返回流的大小。

{any,all,none}Match(Predicate) 返回流的任何/所有元素是否与提供的预期相匹配。

findFirst() 返回流的第一个元素(如果有)。

findAny() 返回流的任何元素(如果有)。

Stream.reduce()方法

聚合操作Stream.reduce()可以将元素序列聚合到该类型的单个元素值。Stream库中有多种reduce()方法,包括了以下两种:

Optional reduce(BinaryOperator op)

T reduce(T identity, BinaryOperator op)

Reduction Operation是一种来自函数编程的技术,它抽象化了许多不同的操作。给定一个类型为 T,包含 n 个元素的非空数列 (x1, x2, ..., xn) 和 T 上的二元运算符(在这里表示为&),reduction opreation 被定义为:(x1&x2&x3...&xn)

二元运算符&需要满足结合性条件:即 ((a&b)&c)==(a&(b&c)),并不需要满足交换性

Optional reduce(BinaryOperator op):只需要确定二元运算符即定义reduction operation的具体操作即可。

清单5:计算总和

int sum = Stream.of(1,2,3,4,5).reduce((a,b)->a+b;).get();

清单6:计算最大值

int max = Stream.of(1,2,3,4,5).reduce(Math::max).get();

T reduce(T identity, BinaryOperator op):使用这个方法时还需要提供一个identity初始值

清单7:字符串串联:

String string = strings.stream().reduce("",String::concat);

Stream.collect()方法

Stream.reduce操作是通过某个连接动作将所有元素汇总成一个汇总结果,比如获取数列的和或它的最大值。但是有时我们并不需要单个汇总值;您想将结果组织为类似 List 或 Map 的数据结构,或者将它汇总为多个汇总值。在这种情况下,可以使用collect()方法。

它接受以下 3 个参数:

collect(Supplier resultSupplier,

BiConsumer accumulator,

BiConsumer combiner)

Supplier resultSupplier:一种生成空结果容器的途径

BiConsumer accumulator:一种将新元素合并到结果容器中的途径

BiConsumer combiner:一种合并两个结果容器的途径

清单8:

List names=list.stream().filter(t->t.getAge()>20).map(t->t.getName()).collect(ArrayList::new,ArrayList::add,ArrayList::addAll);

ArrayList::new 创建一个ArrayList的空容器

ArrayList::add 将新元素添加到结果容器中

ArrayList::addAll 合并两个ArrayList容器

Stream同时还有这样一个collect()方法:

R collect(Collector super T, A, R> collector);

Collector作为参数传入,配合Collectors使用开发更加简便,清单8可以直接调用Collectors.toList()方法

List names=list.stream().filter(t->t.getAge()>20).map(t->t.getName()).collect(Collectors.toList());

Collectors 类常见的聚合操作

toList() 将元素收集到一个 List 中。

toSet() 将元素收集到一个 Set 中。

toCollection(Supplier) 将元素收集到一个特定类型的 Collection 中。

toMap(Function, Function) 将元素收集到一个 Map 中,依据提供的映射函数将元素转换为键值。

summingInt(ToIntFunction) 计算将提供的 int 值映射函数应用于每个元素(以及 long 和 double 版本)的结果的总和。

summarizingInt(ToIntFunction) 计算将提供的 int 值映射函数应用于每个元素(以及 long 和 double 版本)的结果的 sum、min、max、count 和 average。

reducing() 向元素应用缩减(通常用作下游收集器,比如用于 groupingBy)(各种版本)。

partitioningBy(Predicate) 将元素分为两组:为其保留了提供的预期的组和未保留预期的组。

partitioningBy(Predicate, Collector) 将元素分区,使用指定的下游收集器处理每个分区。

groupingBy(Function) 将元素分组到一个 Map 中,其中的键是所提供的应用于流元素的函数,值是共享该键的元素列表。

groupingBy(Function, Collector) 将元素分组,使用指定的下游收集器来处理与每个组有关联的值。

minBy(BinaryOperator) 计算元素的最小值(与 maxBy() 相同)。

mapping(Function, Collector) 将提供的映射函数应用于每个元素,并使用指定的下游收集器(通常用作下游收集器本身,比如用于 groupingBy)进行处理。

joining() 假设元素为 String 类型,将这些元素联结到一个字符串中(或许使用分隔符、前缀和后缀)。

counting() 计算元素数量。(通常用作下游收集器。)

这里主要介绍一下Collectors.groupingBy(),它主要用于对元素的分组操作,同时它也可以通过结合下游收集器实现对元素更加复杂,更加细致的分组操作

1、groupingBy(Function) 将元素分组到一个 Map 中,其中的键是所提供的应用于流元素的函数,值是共享该键的元素列表。

使用Student.age作为Map的key,value则是List

Map> map=list.stream().collect(Collectors.groupingBy(Student::getAge));

2、groupingBy(Function, Collector) 将元素分组,使用指定的下游收集器来处理与每个组有关联的值。

将Student.age作为Map的key,只保留各年龄段身高最高的student

Map> map=list.stream().collect(Collectors.groupingBy(Student::getAge,Collectors.maxBy(Comparator.comparing(Student::getHeight))));

Stream的原理

对于顺序执行,Stream构造了一个 Consumer对象链,其结构与管道结构相符。其中每个 Consumer 对象知道下一个阶段;当它收到一个元素(或被告知没有更多元素)时,它会将 0 或多个元素发送到链中的下一个阶段。例如,与 filter() 阶段有关联的 Consumer 将过滤器谓词应用于输入元素,并将它发送或不发送到下一个阶段;与 map() 阶段有关联的 Consumer 将映射函数应用于输入元素,并将结果发送到下一个阶段。与有状态操作(比如 sorted())有关联的 Consumer 会缓冲元素,直到它看到输入的末尾,然后将排序的数据发送到下一个阶段。Consumer对象链的最后一个阶段将实现终止操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值