web-dev-qa-db-ja.com

Javaストリーム:ブール述語によって2つのリストに分割します

employeesのリストがあります。それらにはisActiveブール型フィールドがあります。 employeesactiveEmployeesformerEmployeesの2つのリストに分けたいと思います。 Stream APIを使用して行うことはできますか?最も洗練された方法は何ですか?

19
Rudziankoŭ

Collectors.partitioningBy

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

結果のマップには、述語が一致したかどうかに対応する2つのリストが含まれます。

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

partitioningBygroupingByよりも使用する理由はいくつかあります( Juan Carlos Mendoza によって示唆されています):

まず、groupingByのパラメーターはFunction<Employee, Boolean>(この場合)であり、nullを返すことができる関数を渡す可能性があります。 つまり、その関数がいずれかの従業員に対してnullを返す場合、3番目のパーティションが存在することになります。 partitioningByPredicate<Employee>を使用するため、2つのパーティションのみを返すことができます。 これにより、コレクターによってNullPointerExceptionがスローされます。明示的に文書化されていませんが、おそらく Map.computeIfAbsent の動作が原因で、nullキーに対して例外が明示的にスローされます。 「関数がnullを返す場合、マッピングは記録されません」、つまり、要素が出力から黙って削除されることを意味します。 (これを指摘してくれた lczapski に感謝します)。

次に、partitioningByを使用して、結果のマップで2つのリスト(*)を取得します。 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 で追加されました。

16
Andy Turner

最も洗練された方法は何ですか?

もちろん、Java 12と新しいCollectors::teeing

List<List<Emploee>> divided = employees.stream().collect(
      Collectors.teeing(
              Collectors.filtering(Emploee::isActive, Collectors.toList()),
              Collectors.filtering(Predicate.not(Emploee::isActive), Collectors.toList()),
              List::of
      ));

System.out.println(divided.get(0));  //active
System.out.println(divided.get(1));  //inactive
2
Adrian

この場合、 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);
2

サードパーティのライブラリを使用する場合、これは _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();
_

PartitionMutableListPartitionIterable から拡張されるタイプです。 PartitionIterableのすべてのサブタイプには、肯定的な結果getSelected()と否定的な結果getRejected()のコレクションがあります。

注:私はEclipseコレクションのコミッターです。

2
Donald Raab