一、Stream的操作三个步骤

  1. 创建Stream
    1. 一个数据源(如:集合,数组),获取一个流
  2. 中间操作
    1. 一个中间操作链,对数据源的数据进行处理
  3. 终止操作(终端操作)

    1. 一个终止操作,执行中间操作链,并产生结果

      二、获取Stream流的几种方式

      1. @Test
      2. public void test() {
      3. // 1.可以通过Collection系列结合提供的stream() 或 parallelStream()
      4. List<String> list = new ArrayList<>();
      5. Stream<String> stream = list.stream();
      6. Stream<String> stringStream = list.parallelStream();
      7. // 2.通过Arrays中的静态方法stream()获取数组流
      8. Integer[] array = new Integer[10];
      9. Stream<Integer> stream1 = Arrays.stream(array);
      10. // 3.通过Stream类中的静态方法of()
      11. Stream<String> stream2 = Stream.of("aa", "bb", "cc");
      12. // 4.创建无限流
      13. // 4.1 迭代
      14. Stream<Integer> iterate = Stream.iterate(0, x -> x + 2);
      15. iterate.limit(10).forEach(System.out::println);
      16. // 4.2 生成
      17. Stream<Double> generate = Stream.generate(() -> Math.random());
      18. generate.forEach(System.out::println);
      19. }

      三、Stream中常用方法(中间操作)

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

      1、筛选与切片

  • filter()——接收Lambda,从流中排除某些元素。
  • limit(n)——截断流,使其元素不超过非定数量
  • skip(n)——跳过元素,返回一个扔掉了前n个元素的流,若流中的元素不足n个,则返回一个空流。与limit()互补
  • distinct——筛选,通过流所生成元素的hashCode()和equals()去除重复元素

    2、映射

  • map——接收lambda,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个

  • flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

    3、排序

  • sorted()——自然排序

  • sorted(Comparator com)——定制排序

    1. /**
    2. * 排序
    3. * sorted()——自然排序(Comparable)
    4. * sorted(Comparator com)——定制排序
    5. */
    6. @Test
    7. public void testSort() {
    8. List<String> list = Arrays.asList("aaa", "ddd", "bbb", "zz", "fff");
    9. // 自然排序
    10. list.stream()
    11. .sorted()
    12. .forEach(System.out::println); // aaa,bbb,ddd,fff,zz
    13. // 定制排序
    14. List<Person> people = Arrays.asList(new Person("周杰伦", 18), new Person("方文山", 40), new Person("林俊杰", 28), new Person("周杰伦", 20));
    15. people.stream()
    16. .sorted( (p1,p2) -> {
    17. if (p1.getName().equals(p2.getName())) {
    18. return p1.getAge().compareTo(p2.getAge());
    19. } else {
    20. return p1.getName().compareTo(p2.getName());
    21. }
    22. }).forEach(System.out::println);
    23. }

    四、Stream中常用方法(终止操作)

    1. 查找与匹配

  • allMatch()——检查是否匹配所有元素

  • anyMatch()——检查是否至少匹配一个元素
  • noneMatch()——检查是否没有匹配所有元素
  • findFirst()——返回第一个元素
  • findAny()——返回当前流中的任意元素

    1. @Test
    2. public void test() {
    3. List<Person> people = Arrays.asList(new Person("周杰伦", 18), new Person("方文山", 40), new Person("昆凌", 40), new Person("薛之谦", 40));
    4. // 检查是否匹配所有元素
    5. boolean b = people.stream()
    6. .allMatch(e -> e.getName().equals("周杰伦"));
    7. System.out.println(b); // false
    8. // 检查是否至少匹配一个元素
    9. boolean b1 = people.stream()
    10. .anyMatch(e -> e.getName().equals("周杰伦"));
    11. System.out.println(b1); // true
    12. // 检查是否没有匹配所有元素
    13. boolean b2 = people.stream()
    14. .noneMatch(e -> e.getName().equals("林俊杰"));
    15. System.out.println(b2); // true
    16. // 返回第一个元素
    17. Optional<Person> first = people.stream()
    18. .findFirst();
    19. System.out.println(first); // Optional[Person(name=周杰伦, age=18)]
    20. // 返回当前流中的任意元素
    21. for (int i = 0; i < 100; i++) {
    22. Optional<Person> any = people.parallelStream()
    23. .findAny();
    24. System.out.println(any);
    25. }
    26. }
  • count()——返回流中元素的总个数

  • max()——返回流中最大值
  • min()——返回流中最小值

    1. @Test
    2. public void test3() {
    3. List<Person> people = Arrays.asList(new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
    4. // 返回流中元素的总个数
    5. long count = people.stream()
    6. .count();
    7. System.out.println(count); // 4
    8. // 返回流中最大值
    9. Optional<Person> max = people.stream()
    10. .max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
    11. System.out.println(max); // Optional[Person(name=方文山, age=45)]
    12. // 返回流中最小值
    13. Optional<Integer> min = people.stream()
    14. .map(Person::getAge)
    15. .min((p1, p2) -> Integer.compare(p1, p2));
    16. System.out.println(min); // Optional[18]
    17. }

    4、归约

  • reduce(T identity,BinaryOperator)/reduce(BinaryOperator)——可以将流中元素反复结合起来 ,得到一个值

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

      1. @Test
      2. public void test4() {
      3. List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
      4. // 可以将流中元素反复结合起来 ,得到一个值(两个参数的)
      5. Integer reduce = list.stream()
      6. .reduce(0, (x, y) -> x + y); // 第一个参数是起始值,此例中:0作为x,然后从集合中取出第一个元素1,将二者相加,再取第二个元素2.....以此类推
      7. System.out.println(reduce); // 55
      8. // 可以将流中元素反复结合起来 ,得到一个值(一个参数的)
      9. Optional<Integer> reduce2 = list.stream()
      10. .reduce(Integer::sum);
      11. System.out.println(reduce2); // 55
      12. }
  • collect——将流转换为其他形式,接收一个Collector接口的实现,用于stream中元素做汇总的方法。

    • Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List,Set,Map)。但是COllectors实用类提供了很多静态方法,可以方便的创建常见收集器实例。

      1. @Test
      2. public void test5() {
      3. List<Person> people = Arrays.asList(new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
      4. // 将流转换为其他形式,接收一个Collector接口的实现,用于stream中元素做汇总的方法。
      5. List<String> collect = people.stream()
      6. .map(Person::getName)
      7. .collect(Collectors.toList());
      8. collect.forEach(System.out::print); // 周杰伦周杰伦方文山昆凌薛之谦
      9. System.out.println();
      10. System.out.println("-------------------");
      11. Set<String> collect1 = people.stream()
      12. .map(Person::getName)
      13. .collect(Collectors.toSet()); // 做到去重效果
      14. collect1.forEach(System.out::print); // 周杰伦方文山薛之谦昆凌
      15. System.out.println();
      16. System.out.println("-------------------");
      17. HashSet<String> collect2 = people.stream()
      18. .map(Person::getName)
      19. .collect(Collectors.toCollection(HashSet::new)); // Collectors.toCollection() 想放到哪个集合中,就指定哪个集合
      20. collect2.forEach(System.out::print); // 周杰伦方文山薛之谦昆凌
      21. }
      1. @Test
      2. public void test6() {
      3. List<Person> people = Arrays.asList(new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
      4. // 1.总数
      5. Long collect = people.stream()
      6. .collect(Collectors.counting());
      7. System.out.println(collect); // 5
      8. // 2.平均值
      9. Double collect1 = people.stream()
      10. .collect(Collectors.averagingInt(Person::getAge));
      11. System.out.println(collect1); // 32.2
      12. // 总和
      13. Integer collect2 = people.stream()
      14. .collect(Collectors.summingInt(Person::getAge));
      15. System.out.println(collect2); // 161
      16. // 最大值
      17. Optional<Integer> collect3 = people.stream()
      18. .map(Person::getAge)
      19. .collect(Collectors.maxBy((p1, p2) -> Integer.compare(p1, p2)));
      20. System.out.println(collect3); // Optional[45]
      21. // 最小值
      22. Optional<Integer> collect4 = people.stream()
      23. .map(Person::getAge)
      24. .collect(Collectors.minBy((p1, p2) -> Integer.compare(p1, p2)));
      25. System.out.println(collect4); // Optional[18]
      26. }
      1. /**
      2. * 分组
      3. */
      4. @Test
      5. public void test7() {
      6. List<Person> people = Arrays.asList(new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
      7. Map<String, List<Person>> collect = people.stream()
      8. .collect(Collectors.groupingBy(Person::getName)); // 按照姓名分组
      9. // 注意:map不能forEach.必须要获取到他的key或value集合才能forEach
      10. System.out.println(collect); // {周杰伦=[Person(name=周杰伦, age=18), Person(name=周杰伦, age=18)], 薛之谦=[Person(name=薛之谦, age=40)], 方文山=[Person(name=方文山, age=45)], 昆凌=[Person(name=昆凌, age=40)]}
      11. /**
      12. * 输出:
      13. * {
      14. * 周杰伦=[Person(name=周杰伦, age=18), Person(name=周杰伦, age=18)],
      15. * 薛之谦=[Person(name=薛之谦, age=40)],
      16. * 方文山=[Person(name=方文山, age=45)],
      17. * 昆凌=[Person(name=昆凌, age=40)]
      18. * }
      19. */
      20. }
      1. /**
      2. * 多级分组
      3. */
      4. @Test
      5. public void test8() {
      6. List<Person> people = Arrays.asList(new Person("周杰伦", 45),new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
      7. Map<String, Map<Integer, List<Person>>> collect = people.stream()
      8. .collect(Collectors.groupingBy(Person::getName, Collectors.groupingBy(Person::getAge)));
      9. System.out.println(collect);
      10. /**
      11. * 输出:
      12. * {
      13. * 周杰伦={
      14. * 18=[Person(name=周杰伦, age=18), Person(name=周杰伦, age=18)],
      15. * 45=[Person(name=周杰伦, age=45)]
      16. * },
      17. * 薛之谦={40=[Person(name=薛之谦, age=40)]},
      18. * 方文山={45=[Person(name=方文山, age=45)]},
      19. * 昆凌={40=[Person(name=昆凌, age=40)]}
      20. * }
      21. */
      22. }
      1. /**
      2. * 分区
      3. * 满足条件的一个区
      4. * 不满足条件的一个区
      5. */
      6. @Test
      7. public void test9() {
      8. List<Person> people = Arrays.asList(new Person("周杰伦", 45),new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
      9. Map<Boolean, List<Person>> collect = people.stream()
      10. .collect(Collectors.partitioningBy(p -> p.getAge() > 20));
      11. System.out.println(collect);
      12. /**
      13. * 输出:
      14. * {
      15. * false=[Person(name=周杰伦, age=18), Person(name=周杰伦, age=18)],
      16. * true=[Person(name=周杰伦, age=45), Person(name=方文山, age=45), Person(name=昆凌, age=40), Person(name=薛之谦, age=40)]
      17. * }
      18. */
      19. }
      1. @Test
      2. public void test10() {
      3. List<Person> people = Arrays.asList(new Person("周杰伦", 45),new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
      4. IntSummaryStatistics collect = people.stream()
      5. .collect(Collectors.summarizingInt(Person::getAge));
      6. System.out.println(collect.getSum());
      7. System.out.println(collect.getAverage());
      8. System.out.println(collect.getMax());
      9. System.out.println(collect.getMin());
      10. System.out.println(collect.getCount());
      11. /**
      12. * 输出:
      13. * 206
      14. * 34.333333333333336
      15. * 45
      16. * 18
      17. * 6
      18. */
      19. }
      1. @Test
      2. public void test11() {
      3. List<Person> people = Arrays.asList(new Person("周杰伦", 45),new Person("周杰伦", 18),new Person("周杰伦", 18), new Person("方文山", 45), new Person("昆凌", 40), new Person("薛之谦", 40));
      4. String collect = people.stream()
      5. .map(Person::getName)
      6. .collect(Collectors.joining()); // 连接
      7. System.out.println(collect); // 周杰伦周杰伦周杰伦方文山昆凌薛之谦
      8. String collect1 = people.stream()
      9. .map(Person::getName)
      10. .collect(Collectors.joining(",")); // 连接,并且想在名字之间加个“,”
      11. System.out.println(collect1); // 周杰伦,周杰伦,周杰伦,方文山,昆凌,薛之谦
      12. String collect2 = people.stream()
      13. .map(Person::getName)
      14. .collect(Collectors.joining(",","开始——(",")——结束")); // 连接,并且想在名字之间加个“,”,再在收尾加个标识
      15. System.out.println(collect2); // 开始——(周杰伦,周杰伦,周杰伦,方文山,昆凌,薛之谦)——结束
      16. }

      五、并行流与顺序流(串行流)

      1、简介

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

      2、了解Fork/Join框架

      2.1 简介

      Fork/Join框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时 ),再将一个个的小任务运算结果进行join汇总。
      image.png

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

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

      2.3 案例

      1. @Test
      2. public void test12() {
      3. long start = System.currentTimeMillis();
      4. long sum = 0;
      5. for (long i = 0; i < 10000000000L; i++) {
      6. sum += i;
      7. }
      8. System.out.println(sum);
      9. Long end = System.currentTimeMillis();
      10. System.out.println(end-start); // 2297
      11. }
      12. // 并行流
      13. @Test
      14. public void test13() {
      15. Instant start = Instant.now();
      16. long reduce = LongStream.rangeClosed(0, 10000000000L)
      17. .parallel()
      18. .reduce(0, Long::sum);
      19. System.out.println(reduce);
      20. Instant end = Instant.now();
      21. System.out.println(Duration.between(start,end).toMillis()); // 475
      22. }

      六、Optional类

      Optional类(java.util.Optional)是一个容器类,代表一个值存在或不存在,原来用null来表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常。

      常用方法:

      image.png