Java 8 Distinto por propiedad

5 minutos de lectura

Java 8 Distinto por propiedad
ricak

En Java 8, ¿cómo puedo filtrar una colección usando el Stream API comprobando la distinción de una propiedad de cada objeto?

Por ejemplo, tengo una lista de Person objeto y quiero eliminar personas con el mismo nombre,

persons.stream().distinct();

Utilizará la verificación de igualdad predeterminada para un Person objeto, así que necesito algo como,

persons.stream().distinct(p -> p.getName());

Desafortunadamente, el distinct() El método no tiene tal sobrecarga. Sin modificar la verificación de igualdad dentro del Person clase es posible hacer esto de manera sucinta?

Java 8 Distinto por propiedad
que tal

Una alternativa sería ubicar a las personas en un mapa usando el nombre como clave:

persons.collect(Collectors.toMap(Person::getName, p -> p, (p, q) -> p)).values();

Tenga en cuenta que la Persona que se mantiene, en caso de un nombre duplicado, será la primera que se encuentre.

  • @skiwi: ¿crees que hay una manera de implementar distinct() sin esa sobrecarga? ¿Cómo sabría cualquier implementación si ha visto un objeto antes sin recordar realmente todos los valores distintos que ha visto? Así que la sobrecarga de toMap y distinct es muy probable que sea lo mismo.

    – Holger

    19 de mayo de 2014 a las 8:38


  • @Holger Es posible que me haya equivocado allí porque no había pensado en los gastos generales distinct() mismo crea.

    – esquí

    19 mayo 2014 a las 8:50

  • Y obviamente altera el orden original de la lista.

    – Felipe

    7 de noviembre de 2016 a las 18:56

  • @Philipp: podría solucionarse cambiando a persons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();

    – Holger

    17 de noviembre de 2017 a las 8:02

  • @DanielEarwicker esta pregunta es sobre “distinto por propiedad”. Requeriría ordenar la transmisión por la misma propiedad, para poder aprovecharlo. Primero, el OP nunca indicó que la transmisión esté ordenada en absoluto. En segundo lugar, los flujos no pueden detectar si están ordenados por una cierta propiedad. En tercer lugar, no existe una operación de transmisión genuina “distinta por propiedad” para hacer lo que sugiere. En cuarto lugar, en la práctica, solo hay dos formas de obtener un flujo ordenado. Una fuente ordenada (TreeSet) que ya es distinto de todos modos o sorted en el flujo que también almacena en búfer todos los elementos.

    – Holger

    30 sep 2018 a las 10:36

  • Esto se llama el Transformada Schwartziana

    – Stuart Caie

    16 mayo 2014 a las 18:07

  • @StuartCaie No realmente… no hay memorización, y el punto no es el rendimiento, sino la adaptación a la API existente.

    – Marko Topolnik

    16 mayo 2014 a las 20:21


  • com.google.common.base.Equivalence.wrap(S) y com.google.common.base.Equivalence.Wrapper.get() también podrían ayudar.

    – bjmi

    30 de enero de 2017 a las 12:52


  • Puede hacer que la clase contenedora sea genérica y parametrizada mediante una función de extracción de claves.

    – Lii

    28 de julio de 2017 a las 2:00

  • Para ampliar la sugerencia de @bjmi, aquí hay un ejemplo de uso: persons.stream().map(Equivalence.equals().onResultOf(Person::getName)::wrap).distinct().map(Equivalence.Wrapper::get)....

    – super_oso hormiguero

    6 oct 2021 a las 19:52

1646957768 505 Java 8 Distinto por propiedad
Santhosh

Otra solución, usando Set. Puede que no sea la solución ideal, pero funciona

Set<String> set = new HashSet<>(persons.size());
persons.stream().filter(p -> set.add(p.getName())).collect(Collectors.toList());

O si puede modificar la lista original, puede usar eliminar si método

persons.removeIf(p -> !set.add(p.getName()));

  • ¡Esta es la mejor respuesta si no está utilizando bibliotecas de terceros!

    – Manoj Shrestha

    22 de diciembre de 2018 a las 1:08


  • usando la idea genial de que Set.add devuelve verdadero si este conjunto aún no contenía el elemento especificado. +1

    – Luvie

    31 de julio de 2019 a las 10:27

  • Creo que este método no funciona para el procesamiento de secuencias paralelas, ya que no es seguro para subprocesos.

    – LoBo

    25 de marzo de 2020 a las 12:32

  • @LoBo Probablemente no. Esto es solo una idea, que funcionará para casos simples. Los usuarios pueden extenderlo para seguridad/paralelismo de subprocesos.

    – Santosh

    27 de marzo de 2020 a las 17:17

  • Enfoque interesante, pero parece un antipatrón para modificar una colección externa (conjunto) mientras se filtra un flujo en otra colección (personas)…

    –Justin Rowe

    8 de mayo de 2020 a las 9:10

1646957769 265 Java 8 Distinto por propiedad
bromista

Hay un enfoque más simple usando un TreeSet con un comparador personalizado.

persons.stream()
    .collect(Collectors.toCollection(
      () -> new TreeSet<Person>((p1, p2) -> p1.getName().compareTo(p2.getName())) 
));

  • ¡Esta es la mejor respuesta si no está utilizando bibliotecas de terceros!

    – Manoj Shrestha

    22 de diciembre de 2018 a las 1:08


  • usando la idea genial de que Set.add devuelve verdadero si este conjunto aún no contenía el elemento especificado. +1

    – Luvie

    31 de julio de 2019 a las 10:27

  • Creo que este método no funciona para el procesamiento de secuencias paralelas, ya que no es seguro para subprocesos.

    – LoBo

    25 de marzo de 2020 a las 12:32

  • @LoBo Probablemente no. Esto es solo una idea, que funcionará para casos simples. Los usuarios pueden extenderlo para seguridad/paralelismo de subprocesos.

    – Santosh

    27 de marzo de 2020 a las 17:17

  • Enfoque interesante, pero parece un antipatrón para modificar una colección externa (conjunto) mientras se filtra un flujo en otra colección (personas)…

    –Justin Rowe

    8 de mayo de 2020 a las 9:10

También podemos usar RxJava (muy poderoso extensión reactiva Biblioteca)

Observable.from(persons).distinct(Person::getName)

o

Observable.from(persons).distinct(p -> p.getName())

  • Rx es increíble, pero esta es una respuesta pobre. Observable está basado en push mientras que Stream está basado en tirar. stackoverflow.com/questions/30216979/…

    – sdgfsdh

    1 jun 2017 a las 16:25

  • la pregunta solicita una solución java8 que no necesariamente use stream. Mi respuesta muestra que java8 stream api es menos potente que rx api

    – frack

    2 de junio de 2017 a las 0:41

  • Utilizando reactorserá Flux.fromIterable(persons).distinct(p -> p.getName())

    – Rito

    16 de agosto de 2017 a las 14:53

  • La pregunta dice literalmente “usando el Stream API”, no “no necesariamente usando flujo”. Dicho esto, esta es una gran solución al problema XY de filtrar el flujo a valores distintos.

    – M. Justin

    2 mayo 2018 a las 16:43

¿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