简体   繁体   English

Java Stream:通过布尔谓词分为两个列表

[英]Java Stream: divide into two lists by boolean predicate

I have a list of employees .我有一份employees名单。 They have isActive boolean field.他们有isActive布尔字段。 I would like to divide employees into two lists: activeEmployees and formerEmployees .我想将employees分为两个列表: activeEmployees和前formerEmployees Is it possible to do using Stream API?是否可以使用 Stream API? What is the most sophisticated way?最复杂的方法是什么?

Collectors.partitioningBy : Collectors.partitioningBy

Map<Boolean, List<Employee>> partitioned = 
    listOfEmployees.stream().collect(
        Collectors.partitioningBy(Employee::isActive));

The resulting map contains two lists, corresponding to whether or not the predicate was matched:结果映射包含两个列表,对应于谓词是否匹配:

List<Employee> activeEmployees = partitioned.get(true);
List<Employee> formerEmployees = partitioned.get(false);

There are a couple of reasons to use partitioningBy over groupingBy (as suggested by Juan Carlos Mendoza ):使用partitioningBy不是groupingBy有几个原因(如Juan Carlos Mendoza所建议的):

Firstly, the parameter of groupingBy is a Function<Employee, Boolean> (in this case), and so there is a possibility of passing it a function which can return null, meaning there would be a 3rd partition if that function returns null for any of the employees. 首先, groupingBy的参数是一个Function<Employee, Boolean> (在这种情况下),因此有可能向它传递一个可以返回 null 的函数,这 意味着如果该函数为任何 返回 null, 则将有第三个分区的员工。 partitioningBy uses a Predicate<Employee> , so it can only ever return 2 partitions. partitioningBy使用 Predicate<Employee> ,因此它只能返回 2 个分区。 which would result in a NullPointerException being thrown by the collector: whilst not documented explicitly, an exception is explicitly thrown for null keys, presumably because of the behavior of Map.computeIfAbsent that "If the function returns null no mapping is recorded", meaning elements would otherwise be dropped silently from the output.这将导致收集器抛出NullPointerException :虽然没有明确记录,但为空键显式抛出异常,大概是因为Map.computeIfAbsent的行为“如果函数返回 null 则不记录映射”,意思是元素否则将从输出中静默删除。 (Thanks to lczapski for pointing this out). (感谢lczapski指出这一点)。

Secondly, you get two lists (*) in the resulting map with partitioningBy ;其次,您在结果映射中使用partitioningBy获得两个列表 (*); with groupingBy , you only get key/value pairs where elements map to the given key:使用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: {}

(*) This behavior isn't documented in the Java 8 Javadoc , but it was added for Java 9 . (*) 此行为未记录在Java 8 Javadoc 中,但已为Java 9添加。

You can also use groupingBy in this case as there are 2 group posibilities (active and inactive employees):在这种情况下,您也可以使用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);

What is the most sophisticated way?最复杂的方法是什么?

Java 12 of course with new Collectors::teeing 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

If you are open to using a third-party library, this will work using Collectors2.partition from Eclipse Collections .如果您愿意使用第三方库,这将使用Eclipse Collections 中的Collectors2.partition工作。

PartitionMutableList<Employee> partition =
        employees.stream().collect(
                Collectors2.partition(Employee::isActive, PartitionFastList::new));

List<Employee> activeEmployees = partition.getSelected();
List<Employee> formerEmployees = partition.getRejected();

You can also simplify things using ListIterate .您还可以使用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 . PartitionMutableList是一种从PartitionIterable扩展的类型。 Every subtype of PartitionIterable has a collection for positive results getSelected() and negative results getRejected() . PartitionIterable每个子类型都有一个用于正结果getSelected()和负结果getRejected()的集合。

Note: I am a committer for Eclipse Collections.注意:我是 Eclipse Collections 的提交者。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM