Java Stream - tenji/ks GitHub Wiki

Java Stream

Stream 作为 Java8 的新特性,基于 lambda 表达式,是对集合对象功能的增强,它专注于对集合对象进行各种高效、便利的聚合操作或者大批量的数据操作,提高了编程效率和代码可读性。

一、Stream 原理

将要处理的元素看做一种流,流在管道中传输,并且可以在管道的节点上处理,包括过滤筛选去重排序、聚合等。元素流在管道中经过中间操作的处理,最后由最终操作得到前面处理的结果。

二、操作分类

  • 中间操作

map (mapToInt, flatMap 等), filter, distinct, sorted, peek, limit, skip, parallel, sequential, unordered

  • 终止操作

forEach, forEachOrdered, toArray, reduce, collect, min, max, count, anyMatch, allMatch, noneMatch, findFirst, findAny, iterator

三、生成方式

  • stream():为集合创建串行流
  • parallelStream():为集合创建并行流

四、常见操作

使用用例涉及的类定义:(集合对象为订单对象,主键、订单号、订单类型、总金额)

class OrderInfo {
    private Integer id;

    private String orderNo;

    private Integer type;

    private Double total;

    public OrderInfo(Integer id, String orderNo, Integer type, Double total) {
        this.id = id;
        this.orderNo = orderNo;
        this.type = type;
        this.total = total;
    }
}

4.1 filter(筛选)

方法定义:

Stream<T> filter(Predicate<? super T> predicate);

使用样例:

# 过滤掉总金额小于百万的订单
List<OrderInfo> orderList = list.stream()
    .filter(orderInfo -> orderInfo.getTotal() >= 1000 * 1000)
    .collect(Collectors.toList());

4.2 map(映射)

Stream 的 Map-Reduce 操作是 Java 函数式编程的精华所在,同时也是最为复杂的部分。但一旦你啃下了这块硬骨头,那你就真正熟悉 Java 的函数式编程了。

map 操作又称为映射操作,是处理 Stream 的重要操作。它的作用是将当前 Stream 中的每个元素都映射转换为另一个元素,从而得到一个新的 Stream。转换前后的元素类型也可以不同。

方法定义:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

使用样例:

# 以数组的形式输出订单金额前十的订单
double[] orderArray = list.stream()
    .sorted(Comparator.comparing(OrderInfo::getTotal).reversed())
    .limit(10)
    .mapToDouble(OrderInfo::getTotal)
    .toArray();

map 方法是一个中间操作,作用是将当前 Stream 中的每个元素通过参数 mapper 转换为另一个元素,转换前的元素类型为 T,转换后的元素类型为 R。

4.3 distinct(去重)

方法定义:

Stream<T> distinct();

4.4 sorted(排序)

方法定义:

Stream<T> sorted();

Stream<T> sorted(Comparator<? super T> comparator);

使用样例:

# 按照 ID 进行排序
List<OrderInfo> orderList = list.stream()
    .sorted(Comparator.comparing(OrderInfo::getId))
    .collect(Collectors.toList());
# 按照 ID 进行倒序
List<OrderInfo> orderList = list.stream()
    .sorted(Comparator.comparing(OrderInfo::getId).reversed())
    .collect(Collectors.toList());
# 先按照订单类型排序,再按照订单总金额排序
List<OrderInfo> orderList = list.stream()
    .sorted(Comparator.comparing(OrderInfo::getId).thenComparing(OrderInfo::getTotal))
    .collect(Collectors.toList());
4.4.1 多字段排序

通过 Comparator.thenComparing(Comparator<? super T> other) 实现,可参考以上第三个样例。

4.5 limit(限制返回个数)

Stream 的 limit 方法返回一个新的流,该流的元素被截断为给定的最大长度。limit 方法包含前 n 个元素,其中 n 小于或等于给定的最大大小。

方法定义:

Stream<T> limit(long maxSize);

使用样例:

# 按照订单总金额排序,且只返回前三条数据
List<OrderInfo> orderList = list.stream()
    .sorted(Comparator.comparing(OrderInfo::getTotal)).limit(3)
    .collect(Collectors.toList());

4.6 skip(删除元素)

4.7 reduce(聚合)

reduce 操作(reduction operation),翻译为规约操作,是 Stream 中最复杂的操作

规约操作,是通过重复执行指定的合并操作(combining operation),将 Stream 中的所有元素合并得到一个汇总结果的过程。例如,求和(sum)、求最大或最小值(max / min)、求平均数(average)、求元素总个数(count)、将所有元素汇总到一个列表(collect),这些都属于规约操作。

规约操作都属于终止操作(terminal operations)。

Stream 类库有两个通用的规约操作 reduce()collect()

4.8 min/max(求最小值/最大值)

Stream.max() 根据提供的 Comparator 返回流的最大元素。比较器是一种比较函数,它对某些对象集合施加总排序。 max() 是一种终端操作,它组合流元素并返回摘要结果。因此,max() 是归约(reduce)的一种特殊情况。该方法返回 Optional 实例。

方法定义:

Optional<T> max(Comparator<? super T> comparator);

使用样例:

# 获取金额最大的订单
OrderInfo orderInfo = list.stream()
    .max(Comparator.comparing(OrderInfo::getTotal)).get();

4.9 anyMatch/allMatch/noneMatch(匹配)

五、问题

不执行终止操作,是否可以获取集合的大小?

不可以。因为 streams 可以是无限的或按需生成输出。

⚠️ **GitHub.com Fallback** ⚠️