Comment puis-je vérifier si a Stream
est vide et lever une exception si ce n'est pas le cas, en tant qu'opération non terminale?
En gros, je cherche quelque chose d'équivalent au code ci-dessous, mais sans matérialiser le flux entre les deux. En particulier, le contrôle ne doit pas avoir lieu avant que le flux ne soit effectivement consommé par une opération de terminal.
public Stream<Thing> getFilteredThings() {
Stream<Thing> stream = getThings().stream()
.filter(Thing::isFoo)
.filter(Thing::isBar);
return nonEmptyStream(stream, () -> {
throw new RuntimeException("No foo bar things available")
});
}
private static <T> Stream<T> nonEmptyStream(Stream<T> stream, Supplier<T> defaultValue) {
List<T> list = stream.collect(Collectors.toList());
if (list.isEmpty()) list.add(defaultValue.get());
return list.stream();
}
java
java-8
java-stream
Céphalopode
la source
la source
Réponses:
Si vous pouvez vivre avec des capacités parallèles limitées, la solution suivante fonctionnera:
Voici un exemple de code qui l'utilise:
Le problème avec une exécution parallèle (efficace) est que la prise en charge de la division du
Spliterator
nécessite un moyen thread-safe pour savoir si l'un des fragments a vu une valeur d'une manière thread-safe. Ensuite, le dernier des fragments en cours d'exécutiontryAdvance
doit se rendre compte que c'est le dernier (et il n'a pas non plus pu avancer) à lancer l'exception appropriée. Je n'ai donc pas ajouté de support pour le fractionnement ici.la source
Les autres réponses et commentaires sont corrects en ce que pour examiner le contenu d'un flux, il faut ajouter une opération de terminal, "consommant" ainsi le flux. Cependant, on peut le faire et transformer le résultat en un flux, sans mettre en mémoire tampon tout le contenu du flux. Voici quelques exemples:
En gros, transformez le flux en un
Iterator
pour l'appelerhasNext()
, et si c'est vrai, transformez leIterator
dos en unStream
. Ceci est inefficace en ce que toutes les opérations ultérieures sur le flux passeront par les méthodeshasNext()
et de l'itérateurnext()
, ce qui implique également que le flux est effectivement traité séquentiellement (même s'il est ensuite mis en parallèle). Cependant, cela vous permet de tester le flux sans mettre en mémoire tampon tous ses éléments.Il existe probablement un moyen de le faire en utilisant un
Spliterator
au lieu d'unIterator
. Cela permet potentiellement au flux renvoyé d'avoir les mêmes caractéristiques que le flux d'entrée, y compris une exécution en parallèle.la source
estimatedSize
etcharacteristics
pourrait même améliorer les performances à un seul thread. Il se trouve que j'ai écrit laSpliterator
solution pendant que vous posiez laIterator
solution…tryAdvance
avant leStream
fait transforme la nature paresseuse duStream
en un flux «partiellement paresseux». Cela implique également que la recherche du premier élément n'est plus une opération parallèle, car vous devez d'abord vous séparer et le fairetryAdvance
sur les parties fractionnées simultanément pour faire une véritable opération parallèle, pour autant que je sache. Si la seule opération de terminal estfindAny
ou similaire, cela détruirait laparallel()
demande entière .tryAdvance
avant le flux et devez envelopper chaque partie fractionnée dans un proxy et collecter les informations «hasAny» de toutes les opérations simultanées par vous-même et vous assurer que la dernière opération simultanée lève l'exception souhaitée si le le flux était vide. Beaucoup de trucs…Cela peut être suffisant dans de nombreux cas
la source
Vous devez effectuer une opération de terminal sur le Stream pour que l'un des filtres soit appliqué. Par conséquent, vous ne pouvez pas savoir s'il sera vide jusqu'à ce que vous le consommiez.
Le mieux que vous puissiez faire est de terminer le Stream avec une
findAny()
opération de terminal, qui s'arrêtera lorsqu'il trouvera un élément, mais s'il n'y en a pas, il devra parcourir toute la liste d'entrée pour le découvrir.Cela ne vous aiderait que si la liste d'entrée contient de nombreux éléments et que l'un des premiers passe les filtres, car seul un petit sous-ensemble de la liste devrait être consommé avant de savoir que le Stream n'est pas vide.
Bien sûr, vous devrez toujours créer un nouveau Stream afin de produire la liste de sortie.
la source
anyMatch(alwaysTrue())
, je pense que c'est le plus prochehasAny
.anyMatch(alwaysTrue())
correspond parfaitement à la sémantique prévue de votrehasAny
, vous donnant unboolean
au lieu deOptional<T>
--- mais nous nousalwaysTrue
est un prédicat Guava.anyMatch(e -> true)
puis.Je pense que cela devrait suffire à mapper un booléen
Dans le code c'est:
la source
Stream.anyMatch()
Suivant l'idée de Stuart, cela pourrait être fait avec un
Spliterator
comme ceci:Je pense que cela fonctionne avec des flux parallèles car l'
stream.spliterator()
opération mettra fin au flux, puis le reconstruira si nécessaireDans mon cas d'utilisation, j'avais besoin d'une valeur par défaut
Stream
plutôt qu'une valeur par défaut. c'est assez facile à changer si ce n'est pas ce dont vous avez besoinla source
Spliterator
je n'ai pas réalisé que @Holger avait également une solution, je me demande comment les deux se comparent.