Filtre Java Flux à 1 et 1 seul élément
Je suis en train d'utiliser Java 8 Stream
s pour trouver des éléments dans un LinkedList
. Je veux garantie, toutefois, qu'il existe un et un seul match pour les critères de filtre.
Prendre ce code:
public static void main(String[] args) {
LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));
User match = users.stream().filter((user) -> user.getId() == 1).findAny().get();
System.out.println(match.toString());
}
static class User {
@Override
public String toString() {
return id + " - " + username;
}
int id;
String username;
public User() {
}
public User(int id, String username) {
this.id = id;
this.username = username;
}
public void setUsername(String username) {
this.username = username;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public int getId() {
return id;
}
}
Ce code trouve un User
basée sur leur carte d'identité. Mais il n'y a pas de garanties combien de User
s appariés le filtre.
De changer le filtre de ligne à:
User match = users.stream().filter((user) -> user.getId() < 0).findAny().get();
Va jeter un NoSuchElementException
(bonne!)
Je le voudrais lever une erreur si il y a des correspondances multiples. Est-il un moyen de faire cela?
count()
est un terminal d'exploitation, de sorte que vous ne pouvez pas le faire. Le flux ne peut pas être utilisé après.Ok, merci @ZouZou. Je n'étais pas tout à fait certaine de ce que cette méthode a fait. Pourquoi n'est-il pas
Stream::size
?Comme un flux de données peut être utilisé une seule fois: le calcul de sa taille signifie "itération" sur elle et après que vous ne pouvez pas utiliser le flux plus.
Wow. Que l'un des commentaires m'ont aidé à comprendre
Stream
s tellement plus que j'ai fait avant...C'est quand vous vous rendez compte que vous avez eu besoin d'utiliser un
LinkedHashSet
(en supposant que vous voulez ordre d'insertion conservés) ou un HashSet
tout au long de. Si votre collection est uniquement utilisé pour trouver l'id d'utilisateur unique, alors pourquoi êtes-vous la collecte de tous les autres éléments? Si il y a un potentiel que vous aurez toujours besoin de trouver des id utilisateur, qui doit également être unique, alors pourquoi utiliser une liste et non pas un jeu? Vous êtes à la programmation en arrière. Utilisez le bouton droit de la collecte de l'emploi et de l'enregistrer vous-même ce mal de tête
OriginalL'auteur ryvantage | 2014-03-27
Vous devez vous connecter pour publier un commentaire.
Créer un personnalisé
Collector
Nous utilisons
Collectionneurs.collectingAndThen
pour construire notreCollector
parList
avec leCollectors.toList()
collector.IllegalStateException
silist.size != 1
.Utilisé comme:
Vous pouvez ensuite personnaliser cette
Collector
autant que vous voulez, par exemple donner de l'exception comme argument dans le constructeur, l'ajuster pour permettre à deux valeurs, et plus encore.Une alternative — sans doute moins élégant — solution:
Vous pouvez utiliser une "solution" qui consiste à
peek()
et unAtomicInteger
, mais vraiment vous ne devriez pas l'utiliser.Ce que vous pourriez faire istead est juste de collecte dans un
List
, comme ceci:Iterables.getOnlyElement
permettrait de raccourcir ces solutions et de fournir de meilleurs messages d'erreur. Juste un conseil pour les autres lecteurs qui utilisent déjà Google Goyave.j'ai enveloppé cette idée dans une classe - gist.github.com/denov/a7eac36a3cda041f8afeabcef09d16fc
Merci de ne pas modifier mon code. Il me met dans une situation où j'ai besoin de valider la totalité de ma réponse, que j'ai écrit il y a quatre ans, et je n'ai tout simplement pas le temps pour ça maintenant.
Solitaire édition a été utile et juste, j'ai donc rétabli après l'examen. Les personnes visitant cette réponse aujourd'hui ne se soucient pas comment vous est venu à la réponse, ils n'ont pas besoin de voir l'ancienne version et la nouvelle version et mise à Jour de la section. Que fait votre réponse plus confuse et moins utile. Il est beaucoup mieux de mettre les messages dans une final de l'état, et si les gens veulent voir comment tout cela s'est déroulé, ils peuvent voir le post de l'histoire.
Le code de la réponse est absolument ce que vous avez écrit. Toute l'éditeur a été le nettoyage de votre poste, seulement supprimer d'une version antérieure de la
singletonCollector()
définition obsolète par la version qui reste en poste, et de la renommer entoSingleton()
. Mon Java flux d'expertise est un peu rouillé, mais le changement de nom semble utile pour moi. L'examen de ce changement m'a pris 2 minutes, tops. Si vous n'avez pas de temps pour examiner les modifications, puis-je suggérer que vous demandez à quelqu'un d'autre de le faire à l'avenir, peut-être dans le Java salle de chat?OriginalL'auteur skiwi
Par souci d'exhaustivité, voici le "one-liner" correspondant à @prunge excellente réponse:
Cette obtient le seul élément correspondant de la rivière, jetant
NoSuchElementException
dans le cas où le flux de données est vide, ouIllegalStateException
dans le cas où le flux de données contient plus d'un élément correspondant.Une variante de cette approche évite de lancer une exception début et représente au contraire le résultat comme un
Optional
contenant soit le seul élément, ou rien (vide), si il y a zéro ou plusieurs éléments:get()
àorElseThrow()
J'aime la brièveté de celle-ci, et le fait qu'elle évite la création d'un non-nécessaire instance de Liste à chaque fois qu'il est appelé.
OriginalL'auteur glts
Les autres réponses qui impliquent la rédaction d'un personnalisé
Collector
sont probablement plus efficaces (comme Louis Wasserman du, +1), mais si vous voulez souci de concision, je vous suggère le texte suivant:Puis vérifiez la taille de la liste des résultats.
limit(2)
dans cette solution? Quelle différence cela ferait-il si la liste résultante a été de 2 ou 100? Si il est plus grand que 1.Il s'arrête immédiatement si elle trouve un deuxième match. C'est ce que tous l'engouement des collectionneurs, tout en utilisant plus de code. 🙂
Que diriez-vous d'
Collectors.collectingAndThen(toList(), l -> { if (l.size() == 1) return l.get(0); throw new RuntimeException(); })
Javadoc dit cela à propos de la limite de param:
maxSize: the number of elements the stream should be limited to
. Donc, il ne devrait pas être.limit(1)
au lieu de.limit(2)
?L'énoncé du problème est de s'assurer qu'il y a exactement un (pas plus, pas moins) de l'élément correspondant. Après mon code, on peut tester
result.size()
pour s'assurer qu'il est égal à 1. Si c'est 2, alors il n'y a plus d'un match, donc c'est une erreur. Si le code au lieu de cela n'alimit(1)
, plus d'un match résulterait en un seul élément, qui ne peuvent être distingués à partir de là étant exactement un match. Ce serait manquer une erreur de cas de l'OP était inquiète.OriginalL'auteur Stuart Marks
Goyave fournit
MoreCollectors.onlyElement()
qui fait la bonne chose ici. Mais si vous devez le faire vous-même, vous pouvez rouler vos propresCollector
pour cela:...ou à l'aide de votre propre
Holder
type, au lieu deAtomicReference
. Vous pouvez réutiliser ceCollector
autant que vous le souhaitez.Collector
était la voie à suivre.Juste assez. J'ai été principalement visant pour la vitesse, pas la concision.
Ouais? Pourquoi la vôtre est la plus rapide?
Principalement parce que l'attribution d'un-
List
est plus cher qu'un seul mutable référence.la dernière mise à jour de phrase au sujet de la
MoreCollectors.onlyElement()
devrait en fait être le premier (et peut-être le seul 🙂 )OriginalL'auteur Louis Wasserman
Utilisation de Goyave est
MoreCollectors.onlyElement()
(JavaDoc).Il fait ce que vous voulez et jette un
IllegalArgumentException
si le flux est constitué de deux éléments ou plus, et unNoSuchElementException
si le flux est vide.Utilisation:
MoreCollectors
fait partie de la encore inédits (comme de 2016-12) unreleased version 21.Cette réponse devrait aller haut.
OriginalL'auteur trevorade
La "trappe d'évacuation de l'opération de" qui vous permet de faire des choses bizarres qui ne sont pas pris en charge par les cours d'eau est de demander une
Iterator
:Goyave a une méthode bien pratique pour prendre un
Iterator
et obtenez le seul élément, de le jeter si il y a zéro ou plusieurs éléments, ce qui pourrait remplacer le fond n-1 lignes ici.OriginalL'auteur Brian Goetz
Mise à jour
Suggestion en commentaire de @Holger:
Réponse originale à cette question
L'exception est levée par
Optional#get
, mais si vous avez plus d'un élément qui ne va pas aider. Vous pourriez recueillir les utilisateurs dans une collection qui n'accepte qu'un seul élément, par exemple:qui jette un
java.lang.IllegalStateException: Queue full
, mais qui se sent trop hacky.Ou vous pouvez utiliser une réduction combinée avec une option:
La réduction essentiellement retourne:
Le résultat est ensuite enveloppé dans une option.
Mais la solution la plus simple serait probablement de collecte et de collecte de l', vérifiez que sa taille est de 1 et obtenez le seul élément.
null
) pour empêcher l'utilisationget()
. Malheureusement, votrereduce
ne fonctionne pas comme vous le pensez, considérer unStream
qui anull
éléments, peut-être que vous pensez que vous êtes couvert, mais j'ai peut être[User#1, null, User#2, null, User#3]
, maintenant, il ne va pas lancer une exception je pense que, si je ne me trompe ici.si il y a des éléments null le filtre va jeter un NPE premier.
Puisque vous savez que le flux ne peut pas passer
null
à la fonction de réduction, la suppression de l'identité de la valeur de l'argument de rendre l'ensemble de la traiter avecnull
dans la fonction obsolète:reduce( (u,v) -> { throw new IllegalStateException("More than one ID found"); } )
fait le travail, et même mieux, il retourne déjà unOptional
, eliding la nécessité pour l'appelantOptional.ofNullable
sur le résultat.OriginalL'auteur assylias
Une alternative est l'utilisation de la réduction:
(cet exemple utilise des chaînes de caractères, mais pourrait facilement s'appliquer à tout type d'objet, y compris
User
)Donc, pour le cas avec
User
vous aurait:OriginalL'auteur prunge
À l'aide d'un
Collector
:Utilisation:
Nous retourner un
Facultatif
, puisque nous avons l'habitude ne pouvez pas assumer lesCollection
pour contenir exactement un seul élément. Si vous savez déjà que c'est le cas, appelez le:Ce met à la charge de handeling l'erreur sur l'appelant, comme il se doit.
OriginalL'auteur Lonely Neuron
Goyave a un
Collector
pour cela appeléMoreCollectors.onlyElement()
.OriginalL'auteur Hans
Nous pouvons utiliser RxJava (très puissant extension de réactif bibliothèque)
La unique opérateur déclenche une exception si aucun utilisateur ou de plus d'un utilisateur est trouvé.
OriginalL'auteur frhack
Comme
Collectors.toMap(keyMapper, valueMapper)
utilise un lancer de fusion pour gérer plusieurs entrées avec la même clé, c'est facile:Vous obtiendrez un
IllegalStateException
pour des doubles de clés. Mais à la fin je ne sais pas si le code ne serait pas encore plus lisible à l'aide d'unif
..collect(Collectors.toMap(user -> "", Function.identity())).get("")
, vous avez un plus générique de comportement.OriginalL'auteur Arne Burmeister
Je suis à l'aide de ces deux collectionneurs:
onlyOne()
jetteIllegalStateException
pour >1 éléments, et NoSuchElementException " (enOptional::get
) pour 0 éléments.Vous peut surcharger les méthodes à prendre un
Supplier
de(Runtime)Exception
.OriginalL'auteur Xavier Dury
Si vous n'avez pas l'esprit à l'aide d'un 3ème partie de la bibliothèque,
SequenceM
de cyclope-flux (etLazyFutureStream
de simple-réagir) à la fois en une seule & singleOptional opérateurs.singleOptional()
lève une exception si il y a0
ou plus de1
éléments dans leStream
, sinon, elle retourne la valeur unique.singleOptional()
retourneOptional.empty()
si il n'y a pas de valeurs ou de plus d'une valeur dans leStream
.Divulgation - je suis l'auteur de deux bibliothèques.
OriginalL'auteur John McClean
À l'aide de réduire
C'est le plus simple et flexible que j'ai trouvé (@prunge réponse)
Cette façon, on obtient:
Optional.empty()
si pas présentOriginalL'auteur Fabio Bonfante
Je suis allé avec le direct-approche et juste en œuvre la chose:
avec JUnit test:
Cette mise en œuvre pas des threads.
OriginalL'auteur gerardw
Avez-vous essayé cette
Source: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
count()
n'est pas bon parce qu'il est un terminal de l'opération.Si c'est vraiment un devis, s'il vous plaît ajouter vos sources
OriginalL'auteur pardeep131085