Stream流

一、Stream的中间操作

  • distinct():去重java User.Stream().distinct()

  • filter:过滤java students.stream().filter(student -> "清华大学".equals(student.getSchool())).collect(Collectors.toList());

  • limit:limit操作也类似于SQL语句中的LIMIT关键字,不过相对功能较弱,limit返回包含前n个元素的流,当集合大小小于n时,则返回实际长度。java students.stream().filter(student -> "计算机".equals(student.getMajor())).limit(2).collect(Collectors.toList());

  • .sorted() :排序```java // 状态排序 orderListDTOS.stream().sorted(Comparator.comparing(OrderListDTO::getOrderStatusStr)

    1. // 评价时间 为null靠前
    2. .thenComparing(OrderListDTO::getEvaluateTime, Comparator.nullsFirst(String::compareTo).reversed())
    3. // 下单时间 倒序
    4. .thenComparing(OrderListDTO::getOrderTime, Comparator.nullsFirst(String::compareTo)).reversed())
    5. // 赋值给新集合 进行保存
    6. .collect(Collectors.toList()).forEach(order -> {
    7. OrderListDTO orderListDTO = new OrderListDTO();
    8. BeanUtils.copyProperties(order, orderListDTO);
    9. orders.add(orderListDTO);
    10. });

    ```

  • skip:是跳过前n个元素java students.stream().filter(student -> "计算机科学".equals(student.getMajor())).skip(2)

  • of:实现判断```java String status = pouch.getStatus(); if (Stream.of(

    1. "Finalized",
    2. "Ready",
    3. "Checkout",
    4. "Confirmed",
    5. "Book",
    6. "Started",
    7. "Inital",
    8. "Close")
    9. .anyMatch(status::equals)) {// anyMatch:检测是否存在一个或多个满足指定的参数行为

    // 逻辑代码块 } ```

二、终端操作:

2.1 查找:

  • allMatch:allMatch用于检测是否全部都满足指定的参数行为,如果全部满足则返回true,如下:学生年龄是否大于等于18。java boolean isAdult = students.stream().allMatch(student -> student.getAge() >= 18);

  • anyMatch:anyMatch则是检测是否存在一个或多个满足指定的参数行为,如果满足则返回true,如下:所有学生中是否有清华大学的学生。java boolean hasWhu = students.stream().anyMatch(student -> "清华大学".equals(student.getSchool()));

  • noneMathch:noneMatch用于检测是否不存在满足指定行为的元素,如果不存在则返回true,如下:检测是否不存在专业为计算机科学的学生。java boolean noneCs = students.stream().noneMatch(student -> "计算机科学".equals(student.getMajor()));

  • findFirst用于返回满足条件的第一个元素java students.stream().filter(student -> "计算机科学".equals(student.getMajor())).findFirst();

  • findAny相对于findFirst的区别在于,findAny不一定返回第一个,而是返回任意一个java students.stream().filter(student -> "计算机".equals(student.getMajor())).findAny();

    实际上对于顺序流式处理而言,findFirst和findAny返回的结果是一样的,至于为什么会这样设计,是因为在下一篇我们介绍的并行流式处理,当我们启用并行流式处理的时候,查找第一个元素往往会有很多限制,如果不是特别需求,在并行流式处理中使用findAny的性能要比findFirst好

2.2 归约:

我们大部分都是通过collect(Collectors.toList())对数据封装返回,如我的目标不是返回一个新的集合,而是希望对经过参数化操作后的集合进行进一步的运算,那么我们可用对集合实施归约操作。java8的流式处理提供了reduce方法来达到这一目的。

前面我们通过mapToInt将Stream<Student>映射成为IntStream,并通过IntStream的sum方法求得所有学生的年龄之和,实际上我们通过归约操作,也可以达到这一目的,实现如下:

  1. // 前面例子中的方法
  2. int totalAge = students.stream()
  3. .filter(student -> "计算机科学".equals(student.getMajor()))
  4. .mapToInt(Student::getAge).sum();
  5. // 归约操作
  6. int totalAge = students.stream()
  7. .filter(student -> "计算机科学".equals(student.getMajor()))
  8. .map(Student::getAge)
  9. .reduce(0, (a, b) -> a + b);
  10. // 进一步简化
  11. int totalAge2 = students.stream()
  12. .filter(student -> "计算机科学".equals(student.getMajor()))
  13. .map(Student::getAge)
  14. .reduce(0, Integer::sum);
  15. // 采用无初始值的重载版本,需要注意返回Optional
  16. Optional<Integer> totalAge = students.stream()
  17. .filter(student -> "计算机科学".equals(student.getMajor()))
  18. .map(Student::getAge)
  19. .reduce(Integer::sum); // 去掉初始值

2.3 收集:

前面利用collect(Collectors.toList())是一个简单的收集操作,是对处理结果的封装,对应的还有toSettoMap,以满足我们对于结果组织的需求。这些方法均来自于java.util.stream.Collectors,我们可以称之为收集器。

2.3.1 规约:

收集器也提供了相应的归约操作,但是与reduce在内部实现上是有区别的,收集器更加适用于可变容器上的归约操作,这些收集器广义上均基于Collectors.reducing()实现。

(1). 求学生总数:.count()、Collectors.counting()

  1. long count = students.stream().collect(Collectors.counting());
  2. // 进一步简化
  3. long count = students.stream().count();

(2). 求最大值和最小值:Collectors.maxBy、Collectors.minBy

  1. // 求最大年龄
  2. Optional<Student> olderStudent = students.stream().collect(Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge()));
  3. // 进一步简化
  4. Optional<Student> olderStudent2 = students.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge)));
  5. // 求最小年龄
  6. Optional<Student> olderStudent3 = students.stream().collect(Collectors.minBy(Comparator.comparing(Student::getAge)));

(3). 求总和:Collectors.summingInt

  1. int totalAge4 = students.stream().collect(Collectors.summingInt(Student::getAge));
  2. // 对应的还有summingLong、summingDouble。
  1. 求平均值:**Collectors.averagingInt**
  1. double avgAge = students.stream().collect(Collectors.averagingInt(Student::getAge));
  2. // 对应的还有averagingLong、averagingDouble。
  1. 一次性得到元素个数、总和、均值、最大值、最小值:
  1. IntSummaryStatistics statistics = students.stream().collect(Collectors.summarizingInt(Student::getAge));
  2. // 输出:
  3. IntSummaryStatistics{count=10, sum=220, min=20, average=22.000000, max=24}

对应的还有summarizingLongsummarizingDouble

(4). 字符串拼接 : Collectors.joining(“”)

  1. String names = students.stream().map(Student::getName).collect(Collectors.joining());
  2. // 输出:孔明伯约玄德云长翼德元直奉孝仲谋鲁肃丁奉
  3. String names = students.stream().map(Student::getName).collect(Collectors.joining(", "));
  4. // 输出:孔明, 伯约, 玄德, 云长, 翼德, 元直, 奉孝, 仲谋, 鲁肃, 丁奉

2.3.2 分组:

在数据库操作中,我们可以通过GROUP BY关键字对查询到的数据进行分组,java8的流式处理也为我们提供了这样的功能Collectors.groupingBy来操作集合:

  1. students.stream().collect(
  2. Collectors.groupingBy(Student::getSchool, // 一级分组,按学校
  3. Collectors.groupingBy(Student::getMajor))); // 二级分组,按专业

实际上在groupingBy的第二个参数不是只能传递groupingBy,还可以传递任意Collector类型,比如我们可以传递一个Collector.counting,用以统计每个组的个数:

  1. Map<String, Long> groups = students.stream().collect(Collectors.groupingBy(Student::getSchool, Collectors.counting()));

如果我们不添加第二个参数,则编译器会默认帮我们添加一个Collectors.toList()

2.3.3 分区:

分区可以看做是分组的一种特殊情况,在分区中key只有两种情况:true或false,目的是将待分区集合按照条件一分为二,处理利用collectors.partitioningBy()方法实现分区,该方法接收一个谓词,例如我们希望将学生分为武大学生和非武大学生,那么可以实现如下:

  1. Map<Boolean, List<Student>> partition = students.stream().collect(Collectors.partitioningBy(student -> "武汉大学".equals(student.getSchool())));

例如:比如将数组分为奇数和偶数。

三、parallelStream(并行流,相比于Stream更快但是涉及到线程安全)

  1. list.parallelStream().map(e -> e + 3).collect(Collectors.toList());

parallelStream在处理大的数据量的集合时使用,但是线程不安全,可以先map,再.collect(Collectors.toList().collect(Collectors.toList()可以封装一个List集合返回,还可以保证线程安全。

———-错误记录:———-

1、Collectors.toMap value为null报错

  1. Map<String, Integer> map3 = new HashMap<>(0);
  2. map3 = list3.stream().collect(Collectors.toMap(
  3. list3 -> Optional.ofNullable(list3).map(SpGoodsIdAndCount::getGoodsId).orElse("")
  4. , list3 -> Optional.ofNullable(list3).map(SpGoodsIdAndCount::getCount).orElse(0)
  5. , (key1, key2) -> key2));

解析: Collectors.toMap():list集合转换为map,通过Optional判断当key和value为null时赋予默认值。 (key1, key2) -> key2):当key重复时,选择key2作为新的key值。