Java8-Stream API 详解

刺骨的言语ヽ痛彻心扉 2022-04-22 02:38 339阅读 0赞

摘要

 Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。它也不同于 StAX 对 XML 解析的 Stream,也不是 Amazon Kinesis 对大数据实时处理的 Stream。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。

简介

流(Stream)是数据通道,用于操作数据源(集合,数组等)所生成的元素序列
“集合讲的的是数据,流讲的是计算”
注意:
 ① Stream不会存储元素
 ② Stream不会改变源对象,相反他们会返回一个持有结果的新的Stream
 ③ Stream操作是延迟执行的,这意味着他们等到需要结果的时候才会执行(惰性求值)

Stream操作的三个步骤

  1. 创建Stream
    一个数据源(如:集合,数组)获取一个流
  2. 中间操作
    一个中间操作链,对数据源的数据进行处理
  3. 终止操作(终端操作)
    一个终止操作,执行中间操作链,并产生结果
    在这里插入图片描述

一:创建Stream

  1. Collection提供了两个方法.stream()与paralleStream()

    @org.junit.Test
    public void test4(){

    1. List<Integer> list = new ArrayList<>();
    2. Stream<Integer> stream = list.stream();//串行流
    3. Stream<Integer> integerStream = list.parallelStream();//并行流

    }

  2. 通过Arrays中的Stream()获取一个数组流。

    Integer[] integers ={ };
    Stream stream1 = Arrays.stream(integers);

  3. 通过Stream类中静态方法of()

    Stream stream2 = Stream.of(“aaa”, “bbb”);

  4. 创建无限流(无穷的数据)

    1. 生成

      //通过生成器产生5个10以内的随机数,如果不使用limit就会无限生成10以内随机数
      Stream.generate(() -> Math.random() * 10).limit(5).forEach(System.out::println);
      —————输出————
      0.8320556195819129
      6.260534125204207
      7.344094646332503
      0.18490598959698068
      6.392272744710005

    2. 迭代

      //通过迭代的方式(一元运算)生成5个数
      Stream.iterate(0,x->x+2).limit(5).forEach(System.out::println);
      —————————-输出——————
      0
      2
      4
      6
      8

二:中间操作

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

  1. List<Integer> list = Arrays.asList(1,2,3,523,21,55);
  2. Stream<Integer> stream3 = list.stream().filter(x -> {
  3. System.out.println("函数执行");
  4. return x > 10;
  5. // stream3.forEach(System.out::println);

上面的代码没有终止操作,当你运行时不会打印任何东西

①筛选与切片

  1. filter——接收Lambda,从流中排除某些元素

    //filter()中需要使用断言型接口(Predicate)
    List list = Arrays.asList(1,2,3,523,21,55);
    Stream stream3 = list.stream().filter(x -> x > 10);
    stream3.forEach(System.out::println);

  2. limit——截断流,使其元素不超过给定数量

    List list = Arrays.asList(1,2,3,523,21,55);
    Stream stream3 = list.stream().limit(3);
    stream3.forEach(System.out::println);
    ——————————输出——————————-
    1
    2
    3

  3. skip——跳过元素返回一个抛弃了前n个元素的流,若流中元素不满足n个,则返回一个空流,与limit形成互补

    List list = Arrays.asList(1,2,3,523,21,55);
    Stream stream3 = list.stream().skip(3);
    stream3.forEach(System.out::println);
    ——————————输出——————————-
    523
    21
    55

  4. distinct——筛选,通过流所所生成元素的hashCode()和equals()去除重复元素

    List list = Arrays.asList(1,2,3,3,2,4);
    Stream stream3 = list.stream().distinct();
    stream3.forEach(System.out::println);
    ——————————输出——————————-
    1
    2
    3
    4

注意:自定义的实体类使用distinct去重时,一定要先重写hashCode()和equals()

②映射

  1. map——接收Lambda,将元素转换为其他形式或提取信息时,接收一个函数作为参数,该函数被应用到每个元素上,并将其映射成一个新的元素

    1. //map()里面使用函数型接口(Function)
    2. List<String> list = Arrays.asList("aa","bb","cc");
    3. Stream<String> stream3 = list.stream().map(String::toUpperCase);
    4. stream3.forEach(System.out::println);
    5. ----------------------输出-----------------------
    6. AA
    7. BB
    8. CC
    9. ------------------------------------------------
    10. 集合里的每一个元素都会使用到String.toUpperCase()方法
    11. 它是以aa作为一个元素,bb作为一个元素
  2. flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接一个流

    1. List<String> list = Arrays.asList("aa","bb","cc");
    2. Stream<String> stream3 = list.stream().flatMap(l -> {
    3. String[] strings = l.split("");
    4. return Arrays.stream(strings);
    5. });
    6. stream3.forEach(System.out::println);
    7. -------------------输出-----------
    8. a
    9. a
    10. b
    11. b
    12. c
    13. c
    14. -----------------------------------------------
    15. flatMap将原来的流转换为一个新的流并且,是以每一个值为单位的

    在这里插入图片描述

③排序

  1. sorted() 自然排序 按照Comparable的方式

    1. List<String> list = Arrays.asList("aa","cc","bb");
    2. Stream<String> stream3 = list.stream().sorted();
    3. stream3.forEach(System.out::println);
    4. ---------------输出-----------
    5. aa
    6. bb
    7. cc
  2. sorted( Comparator com)定制排序

    1. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    2. Stream<Integer> stream3 = list.stream().sorted(Integer::compare);
    3. stream3.forEach(System.out::println);
    4. --------------输出--------------
    5. 1
    6. 2
    7. 3
    8. 3
    9. 6
    10. 8
    11. 9

终止操作

①查找与匹配

  1. allMatch——检查是否匹配所有元素

    1. //allMatch()里面的时断言型接口(Predicate)
    2. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    3. boolean b = list.stream().allMatch(x -> x > 3);
    4. System.out.println(b);
    5. ------------------输出--------------------
    6. false
    7. //因为不是所有的数都大于3
  2. anyMatch——检查是否有匹配至少一个元素

    1. //anyMatch()里面的时断言型接口(Predicate)
    2. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    3. boolean b = list.stream().anyMatch(x -> x > 3);
    4. System.out.println(b);
    5. ------------------输出--------------------
    6. true
    7. //只要有大于3的数就返回true
  3. noneMatch——检查是否没有匹配的元素

    1. //noneMatch()里面的时断言型接口(Predicate)
    2. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    3. boolean b = list.stream().noneMatch(x -> x > 3);
    4. System.out.println(b);
    5. ------------------输出--------------------
    6. false
    7. //双重否定,返回false就是有匹配的元素
  4. findFirst——返回第一个元素

    1. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    2. Optional<Integer> first = list.stream().findFirst();
    3. System.out.println(first.get());
    4. -----------------输出----------------
    5. 1
  5. findAny——返回当前流中的任意一元素

    1. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    2. Optional<Integer> first = list.stream().findAny();
    3. System.out.println(first.get());
    4. -----------------输出----------------
    5. 1
  6. count——-返回流中元素的总数

    1. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    2. long count = list.stream().count();
    3. System.out.println(count);
    4. -----------------输出----------------
    5. 7
  7. max——返回流中最大值

    1. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    2. Optional<Integer> max = list.stream().max(Integer::compareTo);
    3. System.out.println(max.get());
    4. -----------------输出----------------
    5. 9
  8. min——返回流中的最小值

    1. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    2. Optional<Integer> min = list.stream().min(Integer::compareTo);
    3. System.out.println(min.get());
    4. -----------------输出----------------
    5. 1
  9. forEach——遍历流中的元素

    1. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    2. list.stream().forEach(System.out::println);
    3. -----------------输出----------------
    4. 1
    5. 3
    6. 2
    7. 6
    8. 8
    9. 3
    10. 9
    11. //注意:forEach的迭代操作是由Stream API完成的称为内部迭代
    12. //借助于iterator的方式为外部迭代

②归约

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

    1. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    2. Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
    3. System.out.println(reduce);
    4. -----------------输出----------------
    5. 32
    6. //根据2元运算将所有的数加起来
    7. //首先以0为x,1为y,结果为1,然后1为x,取3为y,结果为4,以4为x...以此类推
  2. reduce(BinaryOpreator)——可以将流中元素反复结合起来,返回Optional< T >

    1. List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
    2. Optional<Integer> reduce = list.stream().reduce((x, y) -> x + y);
    3. System.out.println(reduce.get());
    4. -----------------输出----------------
    5. 32
    6. //原理同上,只是这里没有初始值,直接取1为x
    7. //所以ist就有可能为空,当返回的值可能为空时,结果存储在Optional容器中,避免空指针异常

③收集

collect——将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

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

接下来进行详细介绍
首先创建一个实体类

  1. public class User {
  2. private String name;
  3. private Integer age;
  4. private double salary;
  5. public User(String name, Integer age, double salary) {
  6. this.name = name;
  7. this.age = age;
  8. this.salary = salary;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public Integer getAge() {
  17. return age;
  18. }
  19. public void setAge(Integer age) {
  20. this.age = age;
  21. }
  22. public double getSalary() {
  23. return salary;
  24. }
  25. public void setSalary(double salary) {
  26. this.salary = salary;
  27. }
  28. @Override
  29. public String toString() {
  30. return "User{" +
  31. "name='" + name + '\'' +
  32. ", age=" + age +
  33. ", salary=" + salary +
  34. '}';
  35. }
  36. @Override
  37. public boolean equals(Object o) {
  38. if (this == o) return true;
  39. if (o == null || getClass() != o.getClass()) return false;
  40. User user = (User) o;
  41. return Double.compare(user.salary, salary) == 0 &&
  42. Objects.equals(name, user.name) &&
  43. Objects.equals(age, user.age);
  44. }
  45. @Override
  46. public int hashCode() {
  47. return Objects.hash(name, age, salary);
  48. }
  49. }

在测试类中准备好数据

  1. public class StreamTest {
  2. List<User> user = Arrays.asList(new User("张三",12,1000.00),
  3. new User ("李四",32,4000),
  4. new User ("王五",40,4000),
  5. new User ("王五",40,4000));
  6. }
  1. 根据名称生成一个新的List

    List list = user.stream().map(User::getName).collect(Collectors.toList());
    list.forEach(System.out::println);
    ————————-输出———————
    张三
    李四
    王五
    王五

  2. 根据名称生成一个新的Set

    Set set = user.stream().map(User::getName).collect(Collectors.toSet());
    set.forEach(System.out::println);
    ————————-输出———————
    张三
    李四
    王五

  3. 根据名称生成一个新的HashSet

    HashSet hashSet = user.stream().map(User::getName).collect(Collectors.toCollection(HashSet::new));
    hashSet.forEach(System.out::println);
    ————————-输出———————
    李四
    张三
    王五

  4. 获取流中的元素总数

    Long count = user.stream().collect(Collectors.counting());
    System.out.println(count);
    ————————-输出———————
    4

  5. 根据工资获取平均值

    Double avg = user.stream().collect(Collectors.averagingDouble(User::getSalary));
    System.out.println(avg);
    ————————-输出———————
    3250.0

  6. 根据工资获取总和

    Double sum = user.stream().collect(Collectors.summingDouble(User::getSalary));
    System.out.println(sum);
    ————————-输出———————
    13000.0

  7. 根据工资获取组函数

    DoubleSummaryStatistics sum = user.stream().collect(Collectors.summarizingDouble(User::getSalary));
    System.out.println(sum);
    ————————-输出———————
    DoubleSummaryStatistics{ count=4, sum=13000.000000, min=1000.000000, average=3250.000000, max=4000.000000}

  8. 根据工资获取最大值

    Optional max = user.stream().collect(Collectors.maxBy(Comparator.comparingDouble(User::getSalary)));
    System.out.println(max.get());
    ————————-输出———————
    User{ name=’李四’, age=32, salary=4000.0}

  9. 根据工资获取最小值

    Optional min = user.stream().collect(Collectors.minBy(Comparator.comparingDouble(User::getSalary)));
    System.out.println(min.get());
    ————————-输出———————
    User{ name=’张三’, age=12, salary=1000.0}

  10. 分组

    Map> map = user.stream().collect(Collectors.groupingBy(User::getSalary));
    System.out.println(map);
    ————————-输出———————
    { 4000.0=[User{ name=’李四’, age=32, salary=4000.0}, User{ name=’王五’, age=40, salary=4000.0}, User{ name=’王五’, age=40, salary=4000.0}], 1000.0=[User{ name=’张三’, age=12, salary=1000.0}]}

  11. 多级分组

    Map>> collect = user.stream().collect(Collectors.groupingBy(User::getSalary, Collectors.groupingBy(

    1. u -> {
    2. if ( u.getAge() <= 12) {
    3. return "青年";
    4. } else if ( u.getAge() <= 32) {
    5. return "中年";
    6. } else {
    7. return "老年";
    8. }
    9. }
    10. )));

    System.out.println(collect);
    ————————-输出———————
    { 4000.0={ 老年=[User{ name=’王五’, age=40, salary=4000.0}, User{ name=’王五’, age=40, salary=4000.0}], 中年=[User{ name=’李四’, age=32, salary=4000.0}]}, 1000.0={ 青年=[User{ name=’张三’, age=12, salary=1000.0}]}}

  12. 分区

    Map> collect1 = user.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 3000));
    System.out.println(collect1);
    ————————-输出———————
    { false=[User{ name=’张三’, age=12, salary=1000.0}], true=[User{ name=’李四’, age=32, salary=4000.0}, User{ name=’王五’, age=40, salary=4000.0}, User{ name=’王五’, age=40, salary=4000.0}]}

  13. 连接

    String s = user.stream().map(User::getName).collect(Collectors.joining(“—“));
    System.out.println(s);
    ————————-输出———————
    张三—李四—王五—王五

发表评论

表情:
评论列表 (有 0 条评论,339人围观)

还没有评论,来说两句吧...

相关阅读