4.强大的 Stream API
本文最后更新于 1560 天前,其中的信息可能已经有所发展或是发生改变。

1. 了解 Stream

  • Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*)。
  • Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
  • 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。
  • 简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

2. 什么是 Stream

  • 流(Stream) 到底是什么呢?
    是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
    “集合讲的是数据,流讲的是计算!”
  • 注意:
    • Stream 自己不会存储元素。
    • Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
    • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

3. 创建 Stream

Java8 中的 Collection 接口被扩展,提供了
两个获取流的方法:

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

注:在项目开发过程中,使用流式处理时,在数据量较大的情况下,通过并行流可以开启多个线程来执行处理,parallelStream与Stream的区别在于parallelStream开启了多线程的处理方式,所以当对方法替换的同时,必须关注方法处理的过程中,是否用到线程不安全的类型例如HashMap,ArrayList等待,方法内部使用不会出现线程安全问题,当变量在方法外部定义尤为重要,使用并行流需谨慎,时刻考虑线程安全问题。否则可能造成程序死锁,或数据的准确性。造成的后果完全取决于使用非线程安全类的效果。

3.1 由数组创建流

Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
重载形式,能够处理对应基本类型的数组:

public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)
Arrays.stream(personArr).forEach(person -> System.out.println(person));

3.2 由值创建流

可以使用静态方法 Stream.of(), 通过显示值
创建一个流。它可以接收任意数量的参数。

public static<T> Stream<T> of(T... values) : 返回一个流
Stream.of("a","b","c")

3.3 由函数创建流:创建无限流

  • 可以使用静态方法 Stream.iterate()Stream.generate(), 创建无限流。
  1. 迭代
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) ;
Stream<Integer> integerStream = Stream.iterate(0, (x) -> x + 3);
integerStream.forEach(System.out::println);
  1. 生成
public static<T> Stream<T> generate(Supplier<T> s) ;
Stream<Double> generate = Stream.generate(() -> Math.random());
generate.limit(5).forEach(System.out::println);

4. Stream 的中间操作

  • 个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
  1. 筛选与切片
    image
  2. 映射
    image

* map和flatmap的区别等同list.add()和list.addAll()
3. 排序

image

5. Stream 的终止操作

  • 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
  1. 查找与匹配
    image

    image
  2. 归约
    image
personList.stream().map(p->p.getAge()).reduce(0,(p,p1)->p+p1);
Optional<Integer> reduce = personList.stream().map(p -> p.getAge()).reduce(Integer::sum);
reduce.get();
reduce.orElse(0);
  1. 收集
    image

    PS:最难莫过于Conllector
    Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
    image

    image
List<String> collect = personList.stream().map(p -> p.getName()).collect(Collectors.toList());
personList.stream().map(p->p.getName()).collect(Collectors.toCollection(HashSet::new));
personList.stream().collect(Collectors.averagingInt(p->p.getAge()));
personList.stream().collect(Collectors.counting());
personList.stream().collect(Collectors.summingInt(Person::getAge));
Map<Integer, List<Person>> collect1 = personList.stream().collect(Collectors.groupingBy(p -> p.getAge()));
collect1.keySet().forEach(System.out::println);

注:中间操作产生的是流,终止操作产生的不是流

6. 并行流与串行流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与顺序流之间进行切换。

7. Fork/Join 框架与传统线程池的区别

采用 “工作窃取”模式(work-stealing):当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能.

作者:Yuyy
博客:https://yuyy.info

评论

  1. MiMonarchRD
    Windows Chrome
    4年前
    2020-1-16 11:43:19

    沙发

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇