Java Stream专题

一,什么是Stream流

Stream 流是 Java 8 新提供给开发者的一组操作集合的 API,将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选、排序、聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由终端操作 (terminal operation) 得到前面处理的结果。Stream 流可以极大的提高开发效率,也可以使用它写出更加简洁明了的代码。我自从接触过 Stream 流之后,可以说对它爱不释手。本文将由浅及深带您体验 Stream 流的使用。那么就让我们从流的简单使用来开启体验之旅。

二,流的简单使用

创建流

有以下两种创建流的方式,第一种方式我们使用的会相对较多。

  • 调用集合的 stream() 方法或者 parallelStream() 方法创建流。
  • Stream 类的静态 of() 方法创建流。

1. 创建 Stream 流

List< String> createStream = new ArrayList< String>();
// 顺序流
Stream< String> stream = createStream.stream();
// 并行流
Stream< String> parallelStream = createStream.parallelStream();
// of()方法创建
Stream< String> stringStream = Stream.of(
       createStream.toArray(new String[createStream.size()])); 

使用流

2 、,展示了如何使用 Stream 流筛选未及格学生名单:

清单 2. 使用 Stream 流筛选未及格学生名单

public static void streamImpl(List< Student> students) {
  List< Student> filterStudent = students.stream()
       .filter(one -> one.getScore() <  60).collect(Collectors.toList());
  System.out.println(filterStudent);
}

而使用 Java 7 实现筛选未及格学生名单所需代码相对冗长。

三,流的基础知识

接下来您将了解 Stream 流的基础知识,这部分的内容将有助于您理解流的相关操作。

流的分类

Stream流分为顺序流和并行流,所谓顺序流就是按照顺序对集合中的元素进行处理,而并行流则是使用多线程同时对集合中多个元素进行处理,所以在使用并行流的时候就要注意线程安全的问题了

终端操作和中间操作

终端操作会消费 Stream 流,并且会产生一个结果,比如 iterator() 和 spliterator()。如果一个 Stream 流被消费过了,那它就不能被重用的。

中间操作会产生另一个流。需要注意的是中间操作不是立即发生的。而是当在中间操作创建的新流上执行完终端操作后,中间操作指定的操作才会发生。流的中间操作还分无状态操作和有状态操作两种。

  • 在无状态操作中,在处理流中的元素时,会对当前的元素进行单独处理。比如,过滤操作,因为每个元素都是被单独进行处理的,所有它和流中的其它元素无关。
  • 在有状态操作中,某个元素的处理可能依赖于其他元素。比如查找最小值,最大值,和排序,因为他们都依赖于其他的元素。

流接口

BaseStream 接口

从上面的 UML 图可以看出来 BaseStream 接口是 Stream 流最基础的接口,它提供了所有流都可以使用的基本功能。BaseStream 是一个泛型接口,它有两个类型参数 T 和 S, 其中 T 指定了流中的元素的类型,S 指定了具体流的类型,由 <S extends BaseStream<T,S>> 可以知道 S 必须为 BaseStream 或 BaseStream 子类,换句话说,就是 S 必须是扩展自 BaseStream的。BaseStream 继承了 AutoCloseable 接口,简化了关闭资源的操作,但是像平时我们操作的集合或数组,基本上都不会出现关闭流的情况。下面是 BaseStream 接口下定义的方法的相关解释:

  • Iterator<T> iterator():获取流的迭代器。
  • Spliterator spliterator():获取流的 spliterator
  • boolean isParallel():判断一个流是否是并行流,如果是则返回 true,否则返回 false
  • S sequential():基于调用流返回一个顺序流,如果调用流本身就是顺序流,则返回其本身。
  • S parallel():基于调用流,返回一个并行流,如果调用流本身就是并行流,则返回其本身。
  • S unordered():基于调用流,返回一个无序流。
  • S onClose(Runnable closeHandler):返回一个新流,closeHandler 指定了该流的关闭处理程序,当关闭该流时,将调用这个处理程序。
  • void close():从 AutoCloseable 继承来的,调用注册关闭处理程序,关闭调用流(很少会被使用到)。

清单 4 列举了由 BaseStream 接口派生出来的流接口,包括了 IntStreamLongStreamStream 以及 DoubleStream。其中 Stream 接口最为通用,本文的主要讲解对象也是它。

由 BaseStream 接口派生出的流接口

public interface IntStream extends BaseStream<、Intege、r, IntStream> {}
public interface LongStream extends BaseStream<Long, LongStream> {}
public interface DoubleStream extends BaseStream<Double, DoubleStream> {}
public interface Stream<T> extends BaseStream<T, Stream<T>> {}

Stream 接口

  • Stream filter(Predicate predicate):产生一个新流,其中包含调用流中满足 predicate 指定的谓词元素,即筛选符合条件的元素后重新生成一个新的流。(中间操作)
  • Stream map(Function mapper),产生一个新流,对调用流中的元素应用 mapper,新 Stream 流中包含这些元素。(中间操作)
  • IntStream mapToInt(ToIntFunction mapper):对调用流中元素应用 mapper,产生包含这些元素的一个新 IntStream流。(中间操作)
  • Stream sorted(Comparator comparator):产生一个自然顺序排序或者指定排序条件的新流。(中间操作)
  • void forEach(Consumer action):遍历了流中的元素。(终端操作)
  • Optional min(Comparator comparator) 和 Optional max(Comparator comparator):获得流中最大最小值,比较器可以由自己定义。(终端操作)
  • boolean anyMatch(Predicate<? super T> predicate):判断 Stream 流中是否有任何符合要求的元素,如果有则返回 ture,没有返回 false。(终端操作)
  • Stream<T> distinct(),去重操作,将 Stream 流中的元素去重后,返回一个新的流。(中间操作)

 收集操作相关 API

public interface Stream<T> extends BaseStream<T, Stream<T>> {
  …
<R, A> R collect(Collector<? super T, A, R> collector);
  …
}

其中 R 指定结果的类型,T 指定了调用流的元素类型。内部积累的类型由 A 指定。collector 是一个收集器,指定收集过程如何执行,collect() 方法是一个终端方法。一般情况我们只需要借助 Collectors 中的方法就可以完成收集操作。

Collectors 类是一个最终类,里面提供了大量的静态的收集器方法,借助他,我们基本可以实现各种复杂的功能了。

Collectors
public final class Collectors {
…
public static <T> Collector<T, ?, List<T>> toList() {
…
}
public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
   …
}
…
}

Collectors 给我们提供了非常丰富的收集器,这里只列出来了 toList 和 toMap 两种,其他的可以参考 Collectors 类的源码。toList() 相信您在清单 14 中已经见到了,那么下面将展示如何将一个使用收集操作将一个 List 集合转为 Map

使收集操作将 List 转 Map
public static void list2Map() {
  List<Student> students = initData();
  Map<String, Double> collect = students.stream()
         .collect(Collectors.toMap(one -> one.getName(),
one -> one.getScore()));
  System.out.println(collect);
}

可以看到通过 Stream API 可以很方便地将一个 List 转成了 Map,但是这里有一个地方需要注意。那就是在通过 Stream API 将 List 转成 Map 的时候我们需要确保 key 不会重复,否则转换的过程将会直接抛出异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nathaniel333

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值