我有一个employees
列表。它们有一个isActive
布尔字段。我想将employees
分成两个列表:activeEmployees
和formerEmployees
。是否可以使用Stream API来实现?最复杂的方法是什么?
我有一个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
有几个原因(如Juan Carlos Mendoza所建议的):groupingBy
的参数是一个Function<Employee, Boolean>
(在这种情况下),因此有可能传递一个可以返回null的函数,如果其中任何一个雇员的函数返回null,则会导致第三个分区。partitioningBy
使用Predicate<Employee>
,因此它只能返回2个分区。否则收集器将抛出NullPointerException
:虽然没有明确记录,但显然对于null键会抛出异常,这可能是由于Map.computeIfAbsent
的行为导致的,即“如果函数返回null,则不记录任何映射”,这意味着元素将被静默丢弃(感谢lczapski指出这一点)。partitioningBy
在结果映射中获得两个列表(*);而使用groupingBy
,您只能获得元素映射到给定键的键/值对。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中没有记录,但是它在Java 9中被添加了。
Stream.of(1,2,3,4).collect(groupingBy(x -> x == 3 ? null : x >= 3))
,但执行后返回了一个异常:“java.lang.NullPointerException: element cannot be mapped to a null key”。所以这是不可能的。 - lczapski在这种情况下,您还可以使用groupingBy,因为有两个分组可能性(活动和非活动员工):
Map<Boolean, List<Employee>> grouped = employees.stream()
.collect(Collectors.groupingBy(Employee::isActive));
List<Employee> activeEmployees = grouped.get(true);
List<Employee> formerEmployees = grouped.get(false);
groupingBy
的参数是 Function<Employee, Boolean>
,因此有可能传递一个可以返回 null
的函数,这意味着如果该函数对任何员工返回 null,则会有第三个分区。 partitioningBy
使用 Predicate
,因此它只能返回2个分区。 - Andy TurnergroupingBy
的其他原因-请查看我回答的编辑部分。(抱歉,我绝对不是在试图撕裂你的答案,我实际上通过尝试这两个方法学到了东西!) - Andy TurnerisActive
不会返回 null(就像使用原始布尔值一样)。 - Juan Carlos MendozagroupingBy
有可能实现。 - Andy Turner什么是最复杂的方法?
当然是使用新的Collectors::teeing
功能的Java 12。
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
Collectors2.partition
来自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
是一种继承自PartitionIterable
的类型。每个PartitionIterable
的子类型都有一个用于存储正结果的集合getSelected()
和负结果的集合getRejected()
。
注意:我是Eclipse Collections的committer。
stream.filter()
和 collect()
,就像这样:activeEmployees = employees.stream().filter(Employee::isActive).collect(Collectors.toList());
formerEmployees = employees.stream().filter(employee -> !employee.isActive()).collect(Collectors.toList());