Java Stream:通过布尔谓词分成两个列表
Java Stream: divide into two lists by boolean predicate
我有一个 employees
的列表。他们有 isActive
布尔字段。我想将 employees
分成两个列表:activeEmployees
和 formerEmployees
。是否可以使用 Stream API?什么方法最精巧?
Map<Boolean, List<Employee>> partitioned =
listOfEmployees.stream().collect(
Collectors.partitioningBy(Employee::isActive));
生成的映射包含两个列表,对应于谓词是否匹配:
List<Employee> activeEmployees = partitioned.get(true);
List<Employee> formerEmployees = partitioned.get(false);
使用 partitioningBy
而不是 groupingBy
有几个原因(如 所建议):
首先,groupingBy
的参数是一个 Function<Employee, Boolean>
(在本例中),因此有可能向它传递一个可以 return null 的函数,意味着如果任何员工的函数 return 为空,则将有第三个分区。 partitioningBy
使用 Predicate<Employee>
,因此它只能 return 2 个分区。 这将导致 NullPointerException
被收集器抛出:虽然没有记录明确地,为空键明确抛出异常,大概是因为 Map.computeIfAbsent
that "If the function returns null no mapping is recorded", meaning elements would otherwise be dropped silently from the output. (Thanks to lczapski 指出这一点的行为)。
其次,您在结果地图中得到两个列表 (*) partitioningBy
;使用 groupingBy
,您只会得到 key/value 对,其中元素映射到给定键:
System.out.println(
Stream.empty().collect(Collectors.partitioningBy(a -> false)));
// Output: {false=[], true=[]}
System.out.println(
Stream.empty().collect(Collectors.groupingBy(a -> false)));
// Output: {}
(*) Java 8 Javadoc, but it was added for Java 9.
中未记录此行为
您也可以在这种情况下使用 groupingBy,因为有 2 组可能性(活跃和不活跃的员工):
Map<Boolean, List<Employee>> grouped = employees.stream()
.collect(Collectors.groupingBy(Employee::isActive));
List<Employee> activeEmployees = grouped.get(true);
List<Employee> formerEmployees = grouped.get(false);
如果您愿意使用第三方库,这将使用 Collectors2.partition
from Eclipse Collections。
PartitionMutableList<Employee> partition =
employees.stream().collect(
Collectors2.partition(Employee::isActive, PartitionFastList::new));
List<Employee> activeEmployees = partition.getSelected();
List<Employee> formerEmployees = partition.getRejected();
您还可以使用 ListIterate
来简化事情。
PartitionMutableList<Employee> partition =
ListIterate.partition(employees, Employee::isActive);
List<Employee> activeEmployees = partition.getSelected();
List<Employee> formerEmployees = partition.getRejected();
PartitionMutableList
is a type that extends from PartitionIterable
。 PartitionIterable
的每个子类型都有一个阳性结果 getSelected()
和阴性结果 getRejected()
的集合。
注意:我是 Eclipse Collections 的提交者。
What is the most sophisticated way?
Java 12 当然有新的 Collectors::teeing
List<List<Employee>> divided = employees.stream().collect(
Collectors.teeing(
Collectors.filtering(Employee::isActive, Collectors.toList()),
Collectors.filtering(Predicate.not(Employee::isActive), Collectors.toList()),
List::of
));
System.out.println(divided.get(0)); //active
System.out.println(divided.get(1)); //inactive
一种更简单、更简洁的方法是像这样使用 stream.filter()
和 collect()
:
activeEmployees = employees.stream().filter(Employee::isActive).collect(Collectors.toList());
formerEmployees = employees.stream().filter(employee -> !employee.isActive()).collect(Collectors.toList());
我有一个 employees
的列表。他们有 isActive
布尔字段。我想将 employees
分成两个列表:activeEmployees
和 formerEmployees
。是否可以使用 Stream API?什么方法最精巧?
Map<Boolean, List<Employee>> partitioned =
listOfEmployees.stream().collect(
Collectors.partitioningBy(Employee::isActive));
生成的映射包含两个列表,对应于谓词是否匹配:
List<Employee> activeEmployees = partitioned.get(true);
List<Employee> formerEmployees = partitioned.get(false);
使用 partitioningBy
而不是 groupingBy
有几个原因(如
首先,groupingBy
的参数是一个 Function<Employee, Boolean>
(在本例中),因此有可能向它传递一个可以 return null 的函数,意味着如果任何员工的函数 return 为空,则将有第三个分区。 这将导致 partitioningBy
使用 Predicate<Employee>
,因此它只能 return 2 个分区。NullPointerException
被收集器抛出:虽然没有记录明确地,为空键明确抛出异常,大概是因为 Map.computeIfAbsent
that "If the function returns null no mapping is recorded", meaning elements would otherwise be dropped silently from the output. (Thanks to lczapski 指出这一点的行为)。
其次,您在结果地图中得到两个列表 (*) partitioningBy
;使用 groupingBy
,您只会得到 key/value 对,其中元素映射到给定键:
System.out.println(
Stream.empty().collect(Collectors.partitioningBy(a -> false)));
// Output: {false=[], true=[]}
System.out.println(
Stream.empty().collect(Collectors.groupingBy(a -> false)));
// Output: {}
(*) Java 8 Javadoc, but it was added for Java 9.
中未记录此行为您也可以在这种情况下使用 groupingBy,因为有 2 组可能性(活跃和不活跃的员工):
Map<Boolean, List<Employee>> grouped = employees.stream()
.collect(Collectors.groupingBy(Employee::isActive));
List<Employee> activeEmployees = grouped.get(true);
List<Employee> formerEmployees = grouped.get(false);
如果您愿意使用第三方库,这将使用 Collectors2.partition
from Eclipse Collections。
PartitionMutableList<Employee> partition =
employees.stream().collect(
Collectors2.partition(Employee::isActive, PartitionFastList::new));
List<Employee> activeEmployees = partition.getSelected();
List<Employee> formerEmployees = partition.getRejected();
您还可以使用 ListIterate
来简化事情。
PartitionMutableList<Employee> partition =
ListIterate.partition(employees, Employee::isActive);
List<Employee> activeEmployees = partition.getSelected();
List<Employee> formerEmployees = partition.getRejected();
PartitionMutableList
is a type that extends from PartitionIterable
。 PartitionIterable
的每个子类型都有一个阳性结果 getSelected()
和阴性结果 getRejected()
的集合。
注意:我是 Eclipse Collections 的提交者。
What is the most sophisticated way?
Java 12 当然有新的 Collectors::teeing
List<List<Employee>> divided = employees.stream().collect(
Collectors.teeing(
Collectors.filtering(Employee::isActive, Collectors.toList()),
Collectors.filtering(Predicate.not(Employee::isActive), Collectors.toList()),
List::of
));
System.out.println(divided.get(0)); //active
System.out.println(divided.get(1)); //inactive
一种更简单、更简洁的方法是像这样使用 stream.filter()
和 collect()
:
activeEmployees = employees.stream().filter(Employee::isActive).collect(Collectors.toList());
formerEmployees = employees.stream().filter(employee -> !employee.isActive()).collect(Collectors.toList());