关于java.util.stream包的详细说明

      该包下的类用来支持元素流上的函数式操作,比如集合上的Map-Reduce操作。 如:

int sum = widgets.stream()
          .filter(b -> b.getColor() == RED)
          .mapToInt(b -> b.getWeight())
          .sum();

      这个例子,我们用一个Widget的集合变量widgets作为流的数据源,然后在这个流上执行filter-map-reduce操作,最终获取这个集合中红色的Widget的权重的和。

      这个包中引入的关键抽象是流。Stream,IntStream,LongStream和DoubleStream分别是封装Object,和int,long,double的原始类型的Stream。流和集合的不同点主要在以下方面:

  • 不存储:流没有存储空间,不像数据结构那样存储数据;它只是通过一系列的计算操作从数据结构,数组,生成器函数或者IO渠道等数据源传送元素
  • 本质是函数:在流上的操作会产生一个结构,但是不会修改原数据。在一个通过集合获得的流上执行filter将会得到一个新的流,通过这个新的流能得到没有被过滤的掉的元素,而不是将这些元素从数据源中移除。
  • 延迟搜索:许多流的操作,比如:过滤,映射,或者重复删除等,都可以延迟实现,从而可以获取优化的机会。比如有个需求是:【查找具有三个连续元音的第一个字符串】,就不需要检查所有输入的字符串,找到满足需要的即可。流的操作分为中间操作和最终操作,中间操作用来产生流,而最终操作是指生成新的值或者消费值以完成某个任务。而中间操作总数延迟处理的。
  • 可能没有限制:虽然集合是优先大小的,但是流没有这个现在。一些短路操作,比如limit,findFirst,允许在有限的时间内在无限的流上进行计算。
  • 消费性的:流中的元素在流的生命周期中只允许被访问一次,类似于Iterator,如果需要重新访问数据源中的元素需要重新创建一个流。

      流可以通过多种方式来获取,比如以下方法:

  • 通过调用集合类的stream()或parallelStream()方法来从集合获取流
  • 通过调用Arrays.stream(Object[])方法来从数组获取流
  • 通过Stream类的静态工厂方法获取,如Stream.of,String.iterate
  • 通过BufferedReader的lines方法可以获取文件的行的流
  • 通过Files的list的方法可以获取文件路径的流
  • 通过Random.ints方法可以获取一组随机整数的流
  • JDK中许多其他获取流的方式,如:BitSet.stream,Pattern.splitAsStream,JarFile.stream等

      第三方也可以参考这些方式提供其他数据源的流。

Stream的相关操作和管道

      流的操作分为中间操作和最终操作,组合成流管道。流管道包括数据源(如:集合类,数组,生成器函数,IO的Channel等),后面跟零个或多个中间操作(如:Stream.filter,Stream.map等)和一个最终操作(Stream.forEach,Stream.reduce等)。

      中间操作会返回一个新的Stream,但是具体执行操作是延后的。比如像执行filter,不会立即对Stream中的数据进行过滤,而是返回一个新的Stream,当开始遍历时,这个Stream将提供匹配filter的元素。管道的遍历操作直到最终操作执行时才开始。

      最终操作会遍历Stream中的元素,来产生最终的结果或者对每个元素执行响应的操作,比如:Stream.forEach,IntStream.sum等。最终操作执行完成后,流管道中的数据会被全部消费,无法再遍历。如果还想要重新遍历这些相同的数据,必须从数据源重新获取一个新的流。几乎所有情况下,最终操作是立即执行的,在方法返回之前完成对流中的数据的遍历和管道中的操作;只有iterator和spliterator这两个最终操作不是,他们提供了一个“逃生舱”,可以让客户端任意执行现有的管道中的不满足当前需求的其他任务。

      延迟对流的处理是可以提高效率的;如前面的例子中,管道中包含filter,map,sum操作,这些操作合并到一起,再应用到数据中,最小化中间状态。延迟处理还可以避免不必要的全量数据的检测。例如:【查找第一个长度超过1000字符的字符串】,这个需求,只需要检测足够多的数据来找第一个满足条件的字符串即可,而不需要检测数据源里的所有的元素。这种情况对于数据源的长度是无限的或者非常大是是非常重要的。

      中间操作进一步分为无状态操作和有状态操作。无状态操作,比如:filter和map,在处理新元素时是无法看到前面元素的状态,每个元素都是独立于其他元素上的操作进行处理的。有状态操作,比如:distinct和sorted,在处理新元素时,会合并前面处理过的元素的状态。

      有状态的操作需要在所有数据都输入后才能计算得到结果。比如:无法在所有数据输入之前完成对流的排序操作。在并行计算情况下,某些包含有状态的中间操作可能需要在数据上多次遍历或者缓存大量有意义的数据。当某些仅仅包含无状态的中间操作的管道,无论是在串行还是并行计算中,都可以将多个操作进行合并,在一次遍历中完成全部中间操作的执行,最小化缓存中间数据。

      此外,有些操作被认为是短路操作。如果中间操作是短路操作,在输入流是无限的时,将会得到一个有限的流。如果最终操作是短路的,当输入流是无限的时,它将会在有限的时间里终止。在管道里有个短路操作是必要的,但是还不够,对于处理无限输入的流,需要有个能在有限时间内正常终止操作的条件。

并行化

      显式的for循环遍历元素本质上是串行的。可以通过将计算操作重构为一个管道的聚合操作来促使流使用并行操作,而不是对每个单独的元素执行一些列操作。所有的流都可以以串行或并行的方式来执行。JDK实现的流操作默认创建的流是串行的,除非明确要求并行执行。例如:Collection的stream方法和parallelStream方法分别创建串行和并行的流。其他能创建流的方法,比如:IntStream.range,得到的是串行的流,但是这些流可以通过调用BaseStream.parallel来有效的并行化。 例如:如果要并行执行前面部分的:【计算所有W

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 8引入了java.util.stream.Collectors类,它实现了java.util.stream.Collector接口,并提供了许多方法来对流的元素执行map和reduce操作,或者进行统计操作。 以下是java.util.stream.Collectors类的一些常用方法: 1. toList():将流中的所有元素导出到一个列表中。 ```java import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class ToListTest { public static void main(String[] args) { List<String> list = Stream.of("AA", "BB", "CC").collect(Collectors.toList()); list.forEach(s -> System.out.println(s)); } } ``` 2. toSet():将流中的所有元素导出到一个集合中,去除重复元素。 ```java import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; public class ToSetTest { public static void main(String[] args) { Set<String> set = Stream.of("AA", "BB", "CC").collect(Collectors.toSet()); set.forEach(s -> System.out.println(s)); } } ``` 3. toMap():将流中的元素导出到一个Map中,可以指定key和value的映射关系。 ```java import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; public class ToMapTest { public static void main(String[] args) { Map<String, Integer> map = Stream.of("AA", "BB", "CC").collect(Collectors.toMap(s -> s, s -> s.length())); map.forEach((k, v) -> System.out.println(k + ": " + v)); } } ``` 4. joining():将流中的元素连接成一个字符串。 ```java import java.util.stream.Collectors; import java.util.stream.Stream; public class JoiningTest { public static void main(String[] args) { String result = Stream.of("AA", "BB", "CC").collect(Collectors.joining(", ")); System.out.println(result); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值