1. 概述

Java 8 最令人兴奋的特性之一是 Stream API —— 简单来说,它是一个处理元素序列的强大工具。

StreamEx 是一个为标准 Stream API 提供额外功能库,同时优化了性能。核心特性包括:

更简洁便捷的日常操作实现100% 兼容原生 JDK Stream并行处理友好:所有新特性都尽可能利用并行流优势高性能低开销:用更少代码解决问题时,性能不会劣于原生实现(有时甚至更快)

本教程将展示 StreamEx API 的部分核心功能。

2. 环境搭建

要使用 StreamEx,需要在 pom.xml 中添加依赖:

<dependency>
    <groupId>one.util</groupId>
    <artifactId>streamex</artifactId>
    <version>0.6.5</version>
</dependency>

最新版本可在 Maven Central 获取。

本教程将使用简单的 User 类:

public class User {
    int id;
    String name;
    Role role = new Role();

    // 标准 getter/setter 和构造方法
}

以及 Role 类:

public class Role {
}

3. 收集器快捷方法

Stream 中最常用的终端操作之一是 collect,用于将元素重新打包到指定集合中。但原生写法在简单场景下显得冗余:

users.stream()
  .map(User::getName)
  .collect(Collectors.toList());

3.1. 集合收集

StreamEx 无需显式提供 Collector 即可指定集合类型:

List<String> userNames = StreamEx.of(users)
  .map(User::getName)
  .toList();

⚠️ 当需要比简单收集更复杂的操作时,collect 方法依然可用。

3.2. 高级收集器

groupingBy 的简写形式:

Map<Role, List<User>> role2users = StreamEx.of(users)
  .groupingBy(User::getRole);

这会生成类似 SQL GROUP BY 的 Map 结构。原生写法需要:

Map<Role, List<User>> role2users = users.stream()
  .collect(Collectors.groupingBy(User::getRole));

类似地,Collectors.joining() 也有简写:

StreamEx.of(1, 2, 3)
  .joining("; "); // "1; 2; 3"

4. 元素增删与选择

当处理混合类型列表时,可按类型过滤:

List usersAndRoles = Arrays.asList(new User(), new Role());
List<Role> roles = StreamEx.of(usersAndRoles)
  .select(Role.class)
  .toList();

通过便捷操作在 Stream 首尾添加元素:

List<String> appendedUsers = StreamEx.of(users)
  .map(User::getName)
  .prepend("(none)")
  .append("LAST")
  .toList();

使用 nonNull() 过滤空值,并直接作为 Iterable 使用:

for (String line : StreamEx.of(users).map(User::getName).nonNull()) {
    System.out.println(line);
}

5. 数学运算与原生类型支持

StreamEx 增强了对原生类型的支持,看这个自解释的例子:

short[] src = {1,2,3};
char[] output = IntStreamEx.of(src)
  .map(x -> x * 5)
  .toCharArray();

再比如计算无序 double 数组中相邻元素的差值:

public double[] getDiffBetweenPairs(double... numbers) {
    return DoubleStreamEx.of(numbers)
      .pairMap((a, b) -> b - a)
      .toArray();
}

6. Map 操作

6.1. 按键过滤

通过 Map 创建 Stream 并按值过滤:

Map<String, Role> nameToRole = new HashMap<>();
nameToRole.put("first", new Role());
nameToRole.put("second", null);
Set<String> nonNullRoles = StreamEx.ofKeys(nameToRole, Objects::nonNull)
  .toSet();

6.2. 键值对操作

通过 EntryStream 操作键值对:

public Map<User, List<Role>> transformMap( 
    Map<Role, List<User>> role2users) {
    Map<User, List<Role>> users2roles = EntryStream.of(role2users)
     .flatMapValues(List::stream)
     .invert()
     .grouping();
    return users2roles;
}

EntryStream.of 将 Map 转换为键值流,flatMapValues 展开角色列表,invert 交换键值,最后 grouping 完成反转映射 —— 四步搞定复杂转换。

6.3. 键值映射

独立映射键和值:

Map<String, String> mapToString = EntryStream.of(users2roles)
  .mapKeys(String::valueOf)
  .mapValues(String::valueOf)
  .toMap();

快速转换键值类型,简单粗暴。

7. 文件操作

StreamEx 支持高效文件处理(避免全文件加载),特别适合大文件:

StreamEx.ofLines(reader)
  .remove(String::isEmpty)
  .forEach(System.out::println);

⚠️ 重要:StreamEx 不会自动关闭文件!读写操作后务必手动关闭资源,避免内存泄漏。

8. 总结

本文介绍了 StreamEx 的核心功能,更多技巧可参考其速查表

完整代码示例见 GitHub 仓库


原始标题:Introduction to StreamEx | Baeldung