在Java Stream的开发场景中,我们经常会遇到这样的需求:根据某个判断条件,有时候需要往流中添加一个单独的元素,有时候需要添加一个元素列表,最终把这两种情况的结果合并到同一个流中继续处理。这种条件性合并单值与列表结果的操作,需要结合Stream的特性来实现,避免不必要的循环嵌套。
使用flatMap实现条件性合并
flatMap是处理这类场景最常用的方法,它可以将流中的每个元素转换为另一个流,然后把这些流合并成一个统一的流。我们可以通过判断条件返回不同的流,实现单值和列表的条件性合并。
假设我们有一个用户列表,需要根据用户的类型做不同处理:如果是普通用户,只保留用户本身;如果是管理员用户,除了用户本身,还要额外添加其管理的所有子用户。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class User {
private String name;
private String type;
private List<User> subUsers;
public User(String name, String type) {
this.name = name;
this.type = type;
this.subUsers = new ArrayList<>();
}
public User(String name, String type, List<User> subUsers) {
this.name = name;
this.type = type;
this.subUsers = subUsers;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public List<User> getSubUsers() {
return subUsers;
}
public void setSubUsers(List<User> subUsers) {
this.subUsers = subUsers;
}
}
public class StreamMergeDemo {
public static void main(String[] args) {
// 构造测试数据
User normalUser = new User("张三", "normal");
User adminUser = new User("李四", "admin");
List<User> subUsers = new ArrayList<>();
subUsers.add(new User("王五", "normal"));
subUsers.add(new User("赵六", "normal"));
adminUser.setSubUsers(subUsers);
List<User> userList = new ArrayList<>();
userList.add(normalUser);
userList.add(adminUser);
// 条件性合并单值和列表
List<User> result = userList.stream()
.flatMap(user -> {
if ("admin".equals(user.getType())) {
// 管理员用户:返回包含自身和子用户的流
List<User> mergeList = new ArrayList<>();
mergeList.add(user);
mergeList.addAll(user.getSubUsers());
return mergeList.stream();
} else {
// 普通用户:返回只包含自身的流
return Stream.of(user);
}
})
.collect(Collectors.toList());
// 输出结果
result.forEach(u -> System.out.println(u.getName()));
}
}
上面的代码中,flatMap的lambda表达式根据用户的类型返回不同的流:管理员用户返回包含自身和子用户的流,普通用户返回只包含自身的流,最终所有流会被合并成一个完整的流,得到所有需要处理的最终用户列表。
结合Optional处理可能为空的情况
如果条件判断后,单值或者列表可能存在为空的情况,可以结合Optional来避免空指针异常,同时实现条件性合并。
比如我们需要根据配置决定是否添加某个默认元素,如果配置开关开启,就添加默认元素列表,否则不添加任何元素。
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class OptionalMergeDemo {
public static void main(String[] args) {
boolean enableDefault = true;
List<String> defaultList = List.of("默认项1", "默认项2");
List<String> customList = List.of("自定义项1", "自定义项2");
List<String> result = Stream.concat(
customList.stream(),
Optional.of(enableDefault)
.filter(Boolean::booleanValue)
.map(flag -> defaultList.stream())
.orElseGet(Stream::empty)
).collect(Collectors.toList());
System.out.println(result);
}
}
这里首先使用Optional判断enableDefault是否为true,如果为true则返回默认列表的流,否则返回空流,再通过Stream.concat把自定义列表的流和条件判断后的流合并起来,实现了条件性添加列表的效果。
使用Stream.concat拼接流
当条件判断的逻辑比较简单,只是需要在原有流的基础上,根据条件拼接一个新的流(可以是单值流或者列表流)时,Stream.concat是一个很直接的选择。
比如我们有一个数字列表,需要判断列表中是否存在偶数,如果存在就额外添加一个单独的结果值,不存在就不添加。
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ConcatDemo {
public static void main(String[] args) {
List<Integer> numList = List.of(1, 3, 5, 7);
// 判断是否存在偶数
boolean hasEven = numList.stream().anyMatch(num -> num % 2 == 0);
List<Integer> result = Stream.concat(
numList.stream(),
hasEven ? Stream.of(999) : Stream.empty()
).collect(Collectors.toList());
System.out.println(result);
}
}
上面的代码中,先判断原列表是否存在偶数,然后如果条件成立,就把原列表的流和包含单值999的流拼接起来,否则拼接空流,最终得到合并后的结果。
不同策略的适用场景
我们可以根据实际场景选择合适的合并策略:
- 如果条件判断逻辑是针对流中的每个元素,每个元素都可能需要返回单值或者列表,优先选择
flatMap的方式,逻辑更内聚。 - 如果是在原有流的基础上,根据全局条件额外拼接一个单值或者列表,优先选择
Stream.concat的方式,代码更直观。 - 如果合并的过程中需要处理可能为空的情况,结合
Optional可以避免空指针,让代码更健壮。
注意事项
在使用这些策略的时候,需要注意流的一次性特性,不要重复使用已经执行过终止操作的流。另外,如果使用flatMap处理列表返回,要注意列表本身是否为空,避免空指针异常,可以在返回前做非空判断,或者返回空流代替。
需要注意的是,Stream的操作都是惰性的,合并操作的中间步骤不会立即执行,只有遇到终止操作的时候才会触发整个流的处理逻辑,不用过于担心中间合并步骤的性能问题。
Java_Stream条件性合并单值处理列表结果流操作修改时间:2026-06-23 19:42:50