Java8的新特性--Stream流(二)

五、Stream流

Java8中有两大最为重要的改变。第一是Lambda表达式,另外一个则是Stream API(java.util.stream.*)。

Stream是Java8中处理集合的关键抽象概念。它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

使用Stram API对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。

5.0 什么是Stream流

Stream流和文件流还是有不同的。流到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算!”

注意:

  • 1、Stream自己不会存储元素。
  • 2、Stream不会改变源对象。相反,会返回一个持有结果的新Stream。
  • 3、Stream操作是延迟执行的。即会等到需要结果的时候才执行。
    Java8的新特性--Stream流(二)

5.1 Stream 操作的三个步骤

  • 创建Stream
    一个数据源(如:集合、数组),获取一个流
  • 中间操作
    一个中间操作链,对数据源的数据进行处理
  • 终止操作(终端操作)
    一个终止操作,执行中间操作链,并产生结果
  • Java8的新特性--Stream流(二)

5.2 创建Stream

5.2.1 使用Collection接口获取Stream流

  • default Stream<E> stream():返回一个顺序流
  • default Stream<E> parallelStream():返回一个并行流

5.2.2 使用数组Arrays的静态方法stream() 获取流

  • static <T> Stream<T> stream(T[] array):返回一个流

重载形式,能够处理对应基本类型的数组:

  • public static IntStream stream(int[] array)
  • public static LongStream stream(long[] array)
  • public static DoubleStream stream(Double[] array)

 5.2.3 使用静态方法Stream.of() 获取流,它可以接收任意数量的参数

  • public static <T> Stream<T> of(T... values):返回一个流

 5.2.4 使用静态方法Stream.iterate()和Stream.generate()创建无限流

迭代

  • public static <T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)

生成

  • public static <T> Stream<T> genarate(Supplier<T> s)
    Java8的新特性--Stream流(二)

5.3 Stream的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为”惰性求值“

5.3.1 筛选与切片

  • filter(Predicate p):接收Lamnda,从流中排除某些元素
    Java8的新特性--Stream流(二)
  • distinct():筛选,通过流所生成的hashCode()和equals()去除重复元素
  • limit(long maxSize):截断流,使其元素不超过给定数量
  • skip(long n):跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补。

5.3.2 映射

  • map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
    Java8的新特性--Stream流(二)
  • mapToDouble(ToDoubleFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStram
  • mapToInt(ToIntFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStram
  • mapToLong(ToLongFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStram
  • flatMap(Function f):接收一个函数作为参数,将流中的每个值都缓存另一个流,然后把所有流连接成一个流

5.3.3 排序

  • sorted():产生一个新流,其中按自然排序顺序
  • sorted(Comparator comp):产生一个新流,其中按自比较器顺序顺序

5.4 Stream 的终止操作

终端操作会从流的流水线生成结果,其结果可以是任何不是流的值,例如:List、Integer,甚至是void。

5.4.1 查找与匹配

  • allMatch(Predicate p):检查是否匹配所有元素
  • anyMatch(Predicate p):检查是否至少匹配一个元素
  • noneMatch(Predicate p):检查是否没有匹配所有元素
  • findFirst():返回当前流中的第一个元素
  • findAny():返回当前流中的任意一个元素
  • count():返回当前流中元素总数
  • max(Comparator c):返回当前流中的最大值
  • min(Comparator c):返回当前流中的最小值
  • forEach(Comparator c):内部迭代(Stream API帮你把迭代做了。还有外部迭代,需要使用Collection接口由用户去做迭代)

​​​​5.4.2 归约

  • reduce(T iden, BinaryOperator b):可以将流中元素结合起来,得到一个值。返回
  • reduce(BinaryOperator b):可以将流中元素结合起来,得到一个值。返回Optional<T>

备注:map和reduce的连接通常称为map-reduce模式,因Google用它来进行搜索而出名。

map 将集合类(例如列表)元素进行转换的。还有一个 reduce() 函数可以将所有值合并成一个。 Map 和 Reduce 操作是函数式编程的核心操作,因为其功能, reduce 又被称为折叠操作。另外, reduce并不是一个新的操作,你有可能已经在使用它。 SQL 中类似 sum() 、 avg() 或者 count() 的聚集函数,实际上就是reduce操作,因为它们接收多个值并返回一个值。流 API 定义的 reduce() 函数可以接受 lambda 表达式,并对所有值进行合并。 IntStream 这样的类有类似 average() 、 count() 、 sum() 的内建方法来做 reduce 操作,也有mapToLong() 、 mapToDouble() 方法来做转换。这并不会限制你,你可以用内建方法,也可以自己定义。

​​​​5.4.3 收集

  • collect(Collector c):将流转换为其他形式。接收一个Collection接口的实现,用于给Stram中元素做汇总的方法。

Collection接口中方法实现决定了如何对流执行收集操作(如收集到List、Set、Map)。Collections实用类提供了很多静态方法,可以方便地创建常用地收集器实例,具体方法与实例如下表:
Java8的新特性--Stream流(二)Java8的新特性--Stream流(二)

5.5 并行流和串行流

并行流就是把一个内容分成多个数据块,并用不同地线程分别处理每个数据块的流。

Java8中对并行进行了优化,我们可以很容易的对数据进行并行操作。Sream API可以声明性地通过parallel()与sequential()在并行流与串行流之间进行切换。

5.6 了解Fork/Join框架

Fork/Join框架:在必要地情况下,将一个大任务进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务的结果进行join汇总。
Java8的新特性--Stream流(二)

 Fork/Join框架与传统线程池的区别:采用“工作窃取”模式(work-stealing)

当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。

相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能.

简单来说,就是当自己的任务执行完后,从还有任务的线程中随机偷一个,放到自己队列中执行。

六、Optional类

  • Optional<T>类(java.util.Optional)是一个容器类,代表一个值存在或者不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常。
  • 这是一个可以为 null 的容器对象。如果值存在则 isPresent() 方法会返回 true ,调用 get() 方法会返回该对象。

6.1 常用方法

  • Optional.of(T t):创建一个Optional实例
  • Optional.empty():创建一个空的Optional实例
  • Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例。
  • isPresent():判断是否包含值。true表示包含。
  • ifPresent():如果Optional实例有值,就执行后面的代码,没有就不执行也不会报错。后面可以接收Lambda表达式
  • orElse(T t):如果调用对象包含值,返回该值,否则返回orElse方法传入的参数t。
  • orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值
  • map(Funtion f):如果有值则对其处理,并返回处理后的Optional,否则返回Optional.empty()
  • flatMap(Funtion mapper):与map类似,要求返回值必须是Optional

6.2 常用方法 示例

  • 方法1、Optional.of(T t):创建一个Optional实例
    作用:为非Null的值创建一个Optional
    说明:of方法通过工厂方法创建Optional类。需要注意的是,创建对象时传入的参数不能为null。如果传入的参数说null,则抛出异常
  • 方法二、Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例。
    作用:为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。
    说明:ofNullable底层就是of和empty方法的结合 源码:return value == null ? empty() : of(value);
    Java8的新特性--Stream流(二)
  • 方法三、isPresent():判断是否包含值。
    说明:true表示包含。
  • 方法四、get():如果有值则将其返回i,否则抛出异常NoSuchElementException
    Java8的新特性--Stream流(二)
  • 方法五、ifPresent():如果Optional实例有值,就执行后面的代码,没有就不执行也不会报错。后面可以接收Lambda表达式
    作用:如果Optional实例有值,则为其调用consumer,否则不做处理
    说明:Consumer类,供给型接口。Consumer类包含一个抽象方法,该抽象方法对传入的值进行处理,但没有返回值。
    Java8的新特性--Stream流(二)
  • 方法六、orElse(T t):如果调用对象包含值,返回该值,否则返回orElse方法传入的参数t。
    作用:如果有值则将其返回,否则返回orElse方法传入的参数t。
  • 方法七、orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值
    说明:与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法接收Supplier接口的实现用来生成默认值。
    Java8的新特性--Stream流(二)
  • 方法八、map(Funtion f):如果有值则对其处理,并返回处理后的Optional,否则返回Optional.empty()
    说明:map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。
    Java8的新特性--Stream流(二)
  • 方法九、flatMap(Funtion mapper):与map类似,要求返回值必须是Optional
    说明:flatMap与Map方法类似,区别在于map函数的返回值不同。flatMap与 map(Function)非常类似,区别在于传入方法的 lambda 表达式的返回类型。 map方法中的 lambda 表达式返回值可以是任意类型,在 map 函数返回之前会包装为 Optional 。 但 flatMap方法中的 lambda 表达式返回值必须是 Optionl 实例。
    Java8的新特性--Stream流(二)
  • 方法十、filter():过滤
    作用:如果有值且满足断言条件返回包含该值的Optional,否则返回Optional.empty。
    说明:filter方法通过传入限定条件对Optional实例的值进行过滤。可以传入一个lambda表达式,对于filter函数我们应该传入实现了Predicate接口的lambda表达式。
    Java8的新特性--Stream流(二)

6.3 重复注解与类型注解

 @Repeatable(Xxx.class)
Java8的新特性--Stream流(二)