Dans Java 8, comment puis-je filtrer une collection à l'aide de l' Stream
API en vérifiant la distinction d'une propriété de chaque objet?
Par exemple, j'ai une liste de Person
objets et je veux supprimer des personnes du même nom,
persons.stream().distinct();
Va utiliser la vérification d'égalité par défaut pour un Person
objet, j'ai donc besoin de quelque chose comme,
persons.stream().distinct(p -> p.getName());
Malheureusement, la distinct()
méthode n'a pas une telle surcharge. Sans modifier le contrôle d'égalité à l'intérieur de la Person
classe, est-il possible de le faire succinctement?
Function<? super T, ?>
nonFunction<? super T, Object>
. Il convient également de noter que pour le flux parallèle ordonné, cette solution ne garantit pas quel objet sera extrait (contrairement à la normaledistinct()
). De plus, pour les flux séquentiels, il y a une surcharge supplémentaire sur l'utilisation de CHM (qui est absent dans la solution @nosid). Enfin, cette solution viole le contrat defilter
méthode dont le prédicat doit être sans état comme indiqué dans JavaDoc. Néanmoins voté.distinctByKey
n'a aucune idée de son utilisation dans un flux parallèle. Il utilise CHM dans le cas où il est utilisé en parallèle, bien que cela ajoute des frais généraux dans le cas séquentiel comme Tagir Valeev l'a noté ci-dessus.distinctByKey
. Mais cela fonctionne si vous appelez àdistinctByKey
chaque fois, de sorte qu'il crée à chaque fois une nouvelle instance de prédicat..filter(distinctByKey(...))
. Il exécutera la méthode une fois et renverra le prédicat. Donc, fondamentalement, la carte est déjà réutilisée si vous l'utilisez correctement dans un flux. Si vous rendiez la carte statique, la carte serait partagée pour tous les usages. Donc, si vous avez deux flux utilisant cecidistinctByKey()
, les deux utiliseraient la même carte, ce qui n'est pas ce que vous voulez.CallSite
sera lié à laget$Lambda
méthode - qui renverra une nouvelle instance dePredicate
tout le temps, mais ces instances partageront la même chosemap
etfunction
pour autant que je sache. Très agréable!Une alternative serait de placer les personnes sur une carte en utilisant le nom comme clé:
Notez que la Personne conservée, en cas de nom en double, sera la première rencontrée.
la source
distinct()
sans cette surcharge? Comment une implémentation pourrait-elle savoir si elle a déjà vu un objet sans se souvenir de toutes les valeurs distinctes qu'elle a vues? Ainsi, les frais générauxtoMap
etdistinct
sont très probablement les mêmes.distinct()
créer.persons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();
TreeSet
) qui est déjà distincte de toute façon ousorted
sur le flux qui met également en mémoire tampon tous les éléments.Vous pouvez encapsuler les objets personne dans une autre classe, qui compare uniquement les noms des personnes. Ensuite, vous déballez les objets enveloppés pour obtenir à nouveau un flux de personne. Les opérations de flux peuvent se présenter comme suit:
La classe
Wrapper
pourrait ressembler à ceci:la source
equals
méthode peut être simplifiée pourreturn other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());
Une autre solution, en utilisant
Set
. Ce n'est peut-être pas la solution idéale, mais ça marcheOu si vous pouvez modifier la liste d'origine, vous pouvez utiliser la méthode removeIf
la source
Il existe une approche plus simple en utilisant un TreeSet avec un comparateur personnalisé.
la source
Nous pouvons également utiliser RxJava ( bibliothèque d' extensions réactives très puissante )
ou
la source
Observable
est basé sur push alors qu'ilStream
est basé sur pull. stackoverflow.com/questions/30216979/…Flux.fromIterable(persons).distinct(p -> p.getName())
Stream
API», pas «pas nécessairement en utilisant stream». Cela dit, c'est une excellente solution au problème XY de filtrage du flux en valeurs distinctes.Vous pouvez utiliser le
groupingBy
collecteur:Si vous souhaitez avoir un autre flux, vous pouvez utiliser ceci:
la source
Vous pouvez utiliser la
distinct(HashingStrategy)
méthode dans les collections Eclipse .Si vous pouvez refactoriser
persons
pour implémenter une interface Eclipse Collections, vous pouvez appeler la méthode directement dans la liste.HashingStrategy est simplement une interface de stratégie qui vous permet de définir des implémentations personnalisées d'equals et de hashcode.
Remarque: je suis un committer pour les collections Eclipse.
la source
Je vous recommande d'utiliser Vavr , si vous le pouvez. Avec cette bibliothèque, vous pouvez effectuer les opérations suivantes:
la source
Vous pouvez utiliser la bibliothèque StreamEx :
la source
String
s grâce à l'internement de chaînes, mais cela peut aussi ne pas fonctionner.En étendant la réponse de Stuart Marks, cela peut être fait de manière plus courte et sans carte simultanée (si vous n'avez pas besoin de flux parallèles):
Appelez ensuite:
la source
Collections.synchronizedSet(new HashSet<>())
place. Mais ce serait probablement plus lent qu'avec unConcurrentHashMap
.Approche similaire utilisée par Saeed Zarinfam mais plus de style Java 8 :)
la source
flatMap(plans -> plans.stream().findFirst().stream())
elle évite l'utilisation de get on FacultatifJ'ai fait une version générique:
Un exemple:
la source
Une autre bibliothèque qui prend en charge ceci est jOOλ , et sa
Seq.distinct(Function<T,U>)
méthode:Sous le capot , cela fait pratiquement la même chose que la réponse acceptée .
la source
la source
Mon approche consiste à regrouper tous les objets ayant la même propriété, puis à raccourcir les groupes à la taille 1, puis à les collecter finalement sous la forme d'un
List
.la source
La liste des objets distincts peut être trouvée en utilisant:
la source
La façon la plus simple de l'implémenter est de sauter sur la fonction de tri car elle fournit déjà une option
Comparator
qui peut être créée en utilisant la propriété d'un élément. Ensuite, vous devez filtrer les doublons, ce qui peut être fait en utilisant un étatPredicate
qui utilise le fait que pour un flux trié, tous les éléments égaux sont adjacents:Bien sûr, un état
Predicate
n'est pas sûr pour les threads, mais si tel est votre besoin, vous pouvez déplacer cette logique dans unCollector
et laisser le flux s'occuper de la sécurité des threads lorsque vous utilisez votreCollector
. Cela dépend de ce que vous voulez faire avec le flux d'éléments distincts que vous ne nous avez pas dit dans votre question.la source
Sur la base de la réponse de @ josketres, j'ai créé une méthode utilitaire générique:
Vous pouvez rendre cela plus compatible avec Java 8 en créant un collecteur .
la source
Peut-être sera utile à quelqu'un. J'avais un peu une autre exigence. Avoir la liste des objets
A
de tiers supprimer tous ceux qui ont le mêmeA.b
champ pour le mêmeA.id
(plusieursA
objets avec le mêmeA.id
dans la liste). Partition flux réponse par Tagir Valeev m'a inspiré d'utiliser la coutumeCollector
qui les retoursMap<A.id, List<A>>
. SimpleflatMap
fera le reste.la source
J'ai eu une situation, où j'étais supposé obtenir des éléments distincts de la liste sur la base de 2 clés. Si vous voulez une distinction sur la base de deux clés ou une clé composite, essayez ceci
la source
Dans mon cas, j'avais besoin de contrôler quel était l'élément précédent. J'ai ensuite créé un prédicat avec état où j'ai contrôlé si l'élément précédent était différent de l'élément actuel, dans ce cas, je l'ai conservé.
la source
Ma solution dans cette liste:
Dans ma situation, je veux trouver des valeurs distinctes et les mettre dans la liste.
la source
Alors que la réponse la plus élevée est la meilleure réponse à Java 8, elle est en même temps absolument pire en termes de performances. Si vous voulez vraiment une mauvaise application peu performante, allez-y et utilisez-la. La simple exigence d'extraction d'un ensemble unique de noms de personne doit être obtenue par de simples «pour chacun» et un «ensemble». Les choses empirent encore si la liste dépasse 10.
Considérez que vous avez une collection de 20 objets, comme ceci:
Où vous vous opposez
SimpleEvent
ressemble à ceci:Et pour tester, vous avez un code JMH comme celui-ci, (veuillez noter que j'utilise le même prédicat distinctByKey mentionné dans la réponse acceptée):
Ensuite, vous aurez des résultats de référence comme celui-ci:
Et comme vous pouvez le voir, un simple For-Each a un débit 3 fois supérieur et un score d'erreur inférieur à Java 8 Stream.
Plus le débit est élevé, meilleures sont les performances
la source
la source
Si vous voulez lister les personnes suivantes, ce serait le moyen le plus simple
En outre, si vous souhaitez rechercher une liste de noms distincte ou unique , et non Personne , vous pouvez également utiliser la méthode suivante.
Méthode 1: utilisation
distinct
Méthode 2: utilisation
HashSet
la source
Person
art.Le code le plus simple que vous puissiez écrire:
la source