Java Stream: dividir en dos listas por predicado booleano

4 minutos de lectura

tengo una lista de employees. Ellos tienen isActive campo booleano. me gustaria dividir employees en dos listas: activeEmployees y formerEmployees. ¿Es posible hacerlo usando Stream API? ¿Cuál es la forma más sofisticada?

  • Posible duplicado de ¿Cómo particionar una lista por predicado usando java8?

    – Malte Hartwig

    26 oct 2017 a las 15:47

  • @MalteHartwig nota que duplicado dice “predicado”, pero OP en realidad pregunta sobre la agrupación por una función. Sin embargo, no reabriría si estuviera cerrado así.

    –Andy Turner

    26/10/2017 a las 15:54

avatar de usuario
Andy Turner

Collectors.partitioningBy:

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

El mapa resultante contiene dos listas, correspondientes a si el predicado coincidió o no:

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

Hay un par de razones para usar partitioningBy sobre groupingBy (según lo sugerido por Juan Carlos Mendoza):

En primer lugar, el parámetro de groupingBy es un Function<Employee, Boolean> (en este caso), por lo que existe la posibilidad de pasarle una función que puede devolver nulo, lo que significa que habría una tercera partición si esa función devuelve nulo para cualquiera de los empleados. partitioningBy usa un Predicate<Employee>por lo que solo puede devolver 2 particiones. lo que resultaría en un NullPointerException lanzado por el recopilador: aunque no está documentado explícitamente, se lanza explícitamente una excepción para claves nulas, presumiblemente debido al comportamiento de Map.computeIfAbsent que “Si la función devuelve nulo, no se registra ninguna asignación”, lo que significa que los elementos se eliminarían silenciosamente de la salida. (Gracias a lczapski por señalar esto).

En segundo lugar, obtienes dos listas. partitioningByen el mapa resultante con groupingBy; con

System.out.println(
    Stream.empty().collect(Collectors.partitioningBy(a -> false)));
// Output: {false=[], true=[]}

System.out.println(
    Stream.empty().collect(Collectors.groupingBy(a -> false)));
// Output: {}

solo obtiene pares clave/valor donde los elementos se asignan a la clave dada: Este comportamiento no está documentado en el Java 8 Javadocpero se añadió para

  • Java 9

    .

    y tercero, el mapa que obtienes está optimizado internamente, para contener solo dos teclas.

  • – Eugenio Stream.of(1,2,3,4).collect(groupingBy(x -> x == 3 ? null : x >= 3)) 19 sep 2018 a las 7:00 java.lang.NullPointerException: element cannot be mapped to a null keyTenía curiosidad sobre esto: “pasarle una función que puede devolver un valor nulo, lo que significa que habría una tercera partición si esa función devuelve un valor nulo”. He creado un código que devuelve nulo

    y después de la ejecución se devolvió una excepción:

    . Así que no puede ser verdad.


  • – lczapski 9 de septiembre de 2019 a las 7:25@lczapski interesante, actualizaré la respuesta. eso no es en realidad

    documentado

    aunque.


  • –Andy Turner Map.computeIfAbsent9 de septiembre de 2019 a las 8:52

    @lczapski Supongo que esta restricción viene implícitamente de

    que dice que “Si la función devuelve nulo, no se registra ninguna asignación”.

–Andy Turner 9 de septiembre de 2019 a las 8:58 También puedes usar

Map<Boolean, List<Employee>> grouped = employees.stream()
                .collect(Collectors.groupingBy(Employee::isActive));

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

  • agrupar por en este caso como hay 2 posibilidades de grupo (empleados activos e inactivos): +1, pero tenga en cuenta que debe ser groupingBy levemente Function<Employee, Boolean>cuidado con este enfoque: el parámetro de nulles un partitioningBy por lo que existe la posibilidad de pasarle una función que puede devolver Predicate, lo que significa que habría una tercera partición si esa función devuelve un valor nulo para cualquiera de los empleados. los

    usa un

    por lo que solo puede devolver 2 particiones.


  • –Andy Turner groupingBy 26/10/2017 a las 15:34

    Acabo de experimentar un poco y descubrí que hay otras razones para no usar

    – echa un vistazo a la edición de mi respuesta. (Lo siento, definitivamente no solo estoy tratando de copiar tu respuesta, ¡de hecho aprendí algo al probar los dos!)

  • –Andy Turner isActive 26/10/2017 a las 15:41

    @AndyTurner gracias. Para este caso estoy asumiendo que

    no devolverá nulo (como si usara un booleano primitivo).

  • –Juan Carlos Mendoza 26 de octubre de 2017 a las 15:48 Supongo que ese es el caso también. Solo estoy señalando que está el groupingByposibilidad

    vía

    .

–Andy Turner
26/10/2017 a las 15:53

avatar de usuario

Adrián 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

¿Cuál es la forma más sofisticada?
Java 12 por supuesto con nuevo

avatar de usuario Collectors2.partition donald raab Si está abierto a usar una biblioteca de terceros, esto funcionará usandodesde

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

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

Colecciones de Eclipse ListIterate.

PartitionMutableList<Employee> partition =
        ListIterate.partition(employees, Employee::isActive);

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

PartitionMutableList También puedes simplificar las cosas usando PartitionIterable. PartitionIterable es un tipo que se extiende desde getSelected() . Cada subtipo de getRejected()tiene una colección para resultados positivos

y resultados negativos

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad