En Java 8, ¿cómo puedo filtrar una colección usando la StreamAPI comprobando la distinción de una propiedad de cada objeto?
Por ejemplo, tengo una lista de Personobjetos y quiero eliminar personas con el mismo nombre,
persons.stream().distinct();
Usaré la verificación de igualdad predeterminada para un Personobjeto, así que necesito algo como,
persons.stream().distinct(p -> p.getName());
Lamentablemente, el distinct()método no tiene tal sobrecarga. Sin modificar la verificación de igualdad dentro de la Personclase, ¿es posible hacer esto sucintamente?
fuente

Function<? super T, ?>, noFunction<? super T, Object>. También debe tenerse en cuenta que para la secuencia paralela ordenada, esta solución no garantiza qué objeto se extraerá (a diferencia de lo normaldistinct()). También para las secuencias secuenciales hay una sobrecarga adicional en el uso de CHM (que está ausente en la solución @nosid). Finalmente, esta solución viola el contrato delfiltermétodo cuyo predicado debe ser sin estado como se indica en JavaDoc. Sin embargo, votó positivamente.distinctByKeyno tiene idea de si se está utilizando dentro de una secuencia paralela. Utiliza CHM en caso de que se use en paralelo, aunque esto agrega una sobrecarga en el caso secuencial como Tagir Valeev señaló anteriormente.distinctByKey. Pero funciona si llamadistinctByKeycada vez, de modo que crea una nueva instancia de Predicate cada vez..filter(distinctByKey(...)). Ejecutará el método una vez y devolverá el predicado. Básicamente, el mapa ya se está reutilizando si lo usa correctamente dentro de una secuencia. Si hiciera el mapa estático, el mapa se compartiría para todos los usos. Entonces, si tiene dos transmisiones que usan estodistinctByKey(), ambas usarían el mismo mapa, que no es lo que desea.CallSiteestará vinculado alget$Lambdamétodo, que devolverá una nueva instancia dePredicatetodo el tiempo, pero esas instancias compartirán lo mismomapyfunctionhasta donde yo entiendo. ¡Muy agradable!Una alternativa sería colocar a las personas en un mapa usando el nombre como clave:
Tenga en cuenta que la Persona que se mantiene, en caso de un nombre duplicado, será la primera encontrada.
fuente
distinct()sin esa sobrecarga? ¿Cómo sabría cualquier implementación si ha visto un objeto antes sin recordar todos los valores distintos que ha visto? Por lo tanto, la sobrecarga detoMapydistinctes muy probable que sea la misma.distinct()crea.persons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();TreeSet) que ya es distinta de todos modos osorteden la secuencia que también almacena todos los elementos.Puede ajustar los objetos de persona en otra clase, que solo compara los nombres de las personas. Luego, desenvuelve los objetos envueltos para que una persona vuelva a aparecer. Las operaciones de transmisión pueden tener el siguiente aspecto:
La clase
Wrapperpodría verse de la siguiente manera:fuente
equalsmétodo se puede simplificar areturn other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());Otra solución, usando
Set. Puede que no sea la solución ideal, pero funcionaO si puede modificar la lista original, puede usar el método removeIf
fuente
Hay un enfoque más simple usando un TreeSet con un comparador personalizado.
fuente
También podemos usar RxJava ( biblioteca de extensión reactiva muy potente )
o
fuente
Observableestá basado en push mientras queStreamestá basado en pull. stackoverflow.com/questions/30216979/…Flux.fromIterable(persons).distinct(p -> p.getName())StreamAPI", no "no necesariamente usando stream". Dicho esto, esta es una gran solución al problema XY de filtrar el flujo a valores distintos.Puedes usar el
groupingBycolector:Si desea tener otra transmisión, puede usar esto:
fuente
Puede usar el
distinct(HashingStrategy)método en Eclipse Collections .Si puede refactorizar
personspara implementar una interfaz Eclipse Collections, puede llamar al método directamente en la lista.HashingStrategy es simplemente una interfaz de estrategia que le permite definir implementaciones personalizadas de equals y hashcode.
Nota: Soy un committer para Eclipse Collections.
fuente
Recomiendo usar Vavr , si puedes. Con esta biblioteca puedes hacer lo siguiente:
fuente
Puede usar la biblioteca StreamEx :
fuente
Strings gracias al interning de cadenas, pero también puede no funcionar.Ampliando la respuesta de Stuart Marks, esto se puede hacer de una manera más corta y sin un mapa concurrente (si no necesita flujos paralelos):
Luego llame:
fuente
Collections.synchronizedSet(new HashSet<>())lugar. Pero probablemente sería más lento que con unConcurrentHashMap.Enfoque similar que utilizó Saeed Zarinfam pero más estilo Java 8 :)
fuente
flatMap(plans -> plans.stream().findFirst().stream())él, evita el uso de get on OpcionalHice una versión genérica:
Un ejemplo:
fuente
Otra biblioteca que admite esto es jOOλ , y su
Seq.distinct(Function<T,U>)método:Sin embargo, bajo el capó , hace prácticamente lo mismo que la respuesta aceptada .
fuente
fuente
Mi enfoque para esto es agrupar todos los objetos con la misma propiedad, luego acortar los grupos al tamaño de 1 y luego finalmente reunirlos como a
List.fuente
La lista de objetos distintos se puede encontrar usando:
fuente
La forma más fácil de implementar esto es saltar a la función de clasificación, ya que proporciona una opción
Comparatorque se puede crear utilizando la propiedad de un elemento. Luego, debe filtrar los duplicados que se pueden hacer usando un estado completoPredicateque utiliza el hecho de que para una secuencia ordenada todos los elementos iguales son adyacentes:Por supuesto, un estado completo
Predicateno es seguro para subprocesos, sin embargo, si esa es su necesidad, puede mover esta lógica aCollectory dejar que la transmisión se encargue de la seguridad de subprocesos cuando use suCollector. Esto depende de lo que quiera hacer con la secuencia de elementos distintos que no nos dijo en su pregunta.fuente
Sobre la base de la respuesta de @ josketres, creé un método de utilidad genérico:
Puede hacer que esto sea más compatible con Java 8 creando un recopilador .
fuente
Quizás sea útil para alguien. Tenía un poco otro requisito. Tener una lista de objetos
Ade terceros elimina todos los que tienen el mismoA.bcampo para el mismoA.id(Aobjeto múltiple con el mismoA.iden la lista). La respuesta de partición de flujo de Tagir Valeev me inspiró a usar la costumbreCollectorque regresaMap<A.id, List<A>>. SimpleflatMaphará el resto.fuente
Tuve una situación en la que se suponía que debía obtener elementos distintos de la lista basados en 2 claves. Si desea una distinción basada en dos claves o una clave compuesta, intente esto
fuente
En mi caso, necesitaba controlar cuál era el elemento anterior. Luego creé un predicado con estado donde controlaba si el elemento anterior era diferente del elemento actual, en ese caso lo guardé.
fuente
Mi solución en este listado:
En mi situación, quiero encontrar valores distintos y ponerlos en la lista.
fuente
Si bien la respuesta más votada es absolutamente la mejor respuesta para Java 8, al mismo tiempo es absolutamente peor en términos de rendimiento. Si realmente desea una aplicación de bajo rendimiento, siga adelante y úsela. El simple requisito de extraer un conjunto único de nombres de personas se logrará mediante un mero "para cada uno" y un "conjunto". Las cosas empeoran aún más si la lista supera el tamaño de 10.
Considere que tiene una colección de 20 objetos, como este:
Donde objetas se
SimpleEventve así:Y para probar, tiene un código JMH como este (tenga en cuenta que estoy usando el mismo distintivo distinto de KeyKey mencionado en la respuesta aceptada):
Entonces tendrás resultados de referencia como este:
Y como puede ver, un For-Each simple es 3 veces mejor en rendimiento y menos en puntaje de error en comparación con Java 8 Stream.
Mayor rendimiento, mejor rendimiento
fuente
fuente
Si desea hacer una lista de personas, la siguiente sería
Además, si desea encontrar una lista de nombres distinta o única , no Persona , también puede hacerlo utilizando los dos métodos siguientes.
Método 1: usando
distinctMétodo 2: usando
HashSetfuente
Persons.El código más simple que puedes escribir:
fuente