J'ai une liste de employees
. Ils ont isActive
champ booléen. Je voudrais diviser employees
en deux listes: activeEmployees
et formerEmployees
. Est-il possible de faire en utilisant l'API Stream? Quelle est la manière la plus sophistiquée?
Map<Boolean, List<Employee>> partitioned =
listOfEmployees.stream().collect(
Collectors.partitioningBy(Employee::isActive));
La carte résultante contient deux listes, correspondant à la correspondance ou non du prédicat:
List<Employee> activeEmployees = partitioned.get(true);
List<Employee> formerEmployees = partitioned.get(false);
Il y a plusieurs raisons d'utiliser partitioningBy
plutôt que groupingBy
(comme suggéré par Juan Carlos Mendoza ):
Premièrement, le paramètre de groupingBy
est un Function<Employee, Boolean>
(dans ce cas), et donc il est possible de lui passer une fonction qui peut retourner null, ce qui signifie qu'il y aurait une 3e partition si cette fonction retourne null pour l'un des employés. ce qui entraînerait la génération d'un partitioningBy
utilise un Predicate<Employee>
, il ne peut donc renvoyer que 2 partitions.NullPointerException
par le collecteur: bien qu'elle ne soit pas explicitement documentée, une exception est explicitement levée pour les clés nulles, probablement en raison du comportement de Map.computeIfAbsent
que "Si la fonction renvoie null, aucun mappage n'est enregistré", ce qui signifie que les éléments seraient sinon supprimés silencieusement de la sortie. (Merci à lczapski pour l'avoir signalé).
Deuxièmement, vous obtenez deux listes (*) dans la carte résultante avec partitioningBy
; avec groupingBy
, vous obtenez uniquement des paires clé/valeur où les éléments correspondent à la clé donnée:
System.out.println(
Stream.empty().collect(Collectors.partitioningBy(a -> false)));
// Output: {false=[], true=[]}
System.out.println(
Stream.empty().collect(Collectors.groupingBy(a -> false)));
// Output: {}
(*) Ce comportement n'est pas documenté dans Java 8 Javadoc , mais il a été ajouté pour Java 9 .
Quelle est la manière la plus sophistiquée?
Java 12 bien sûr avec le nouveau 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
Vous pouvez également utiliser groupingBy dans ce cas car il existe 2 possibilités de groupe (employés actifs et inactifs):
Map<Boolean, List<Employee>> grouped = employees.stream()
.collect(Collectors.groupingBy(Employee::isActive));
List<Employee> activeEmployees = grouped.get(true);
List<Employee> formerEmployees = grouped.get(false);
Si vous êtes prêt à utiliser une bibliothèque tierce, cela fonctionnera en utilisant Collectors2.partition
de Collections Eclipse .
PartitionMutableList<Employee> partition =
employees.stream().collect(
Collectors2.partition(Employee::isActive, PartitionFastList::new));
List<Employee> activeEmployees = partition.getSelected();
List<Employee> formerEmployees = partition.getRejected();
Vous pouvez également simplifier les choses en utilisant ListIterate
.
PartitionMutableList<Employee> partition =
ListIterate.partition(employees, Employee::isActive);
List<Employee> activeEmployees = partition.getSelected();
List<Employee> formerEmployees = partition.getRejected();
PartitionMutableList
est un type qui s'étend de PartitionIterable
. Chaque sous-type de PartitionIterable
a une collection pour les résultats positifs getSelected()
et les résultats négatifs getRejected()
.
Remarque: je suis un committer pour les collections Eclipse.