J'ai un ensemble de données représenté par un flux Java 8:
Stream<T> stream = ...;
Je peux voir comment le filtrer pour obtenir un sous-ensemble aléatoire - par exemple
Random r = new Random();
PrimitiveIterator.OfInt coin = r.ints(0, 2).iterator();
Stream<T> heads = stream.filter((x) -> (coin.nextInt() == 0));
Je peux également voir comment je pourrais réduire ce flux pour obtenir, par exemple, deux listes représentant deux moitiés aléatoires de l'ensemble de données, puis les transformer en flux. Mais existe-t-il un moyen direct de générer deux flux à partir du flux initial? Quelque chose comme
(heads, tails) = stream.[some kind of split based on filter]
Merci pour tout aperçu.
java
java-8
java-stream
user1148758
la source
la source
Stream
en plusieursStream
s sans conversion intermédiaire , même si je pense que les personnes qui ont atteint cette question cherchent en fait le moyen d'y parvenir indépendamment de cette contrainte, qui est la réponse de Mark. Cela peut être dû au fait que la question du titre n'est pas la même que celle de la description .Réponses:
Pas exactement. Tu ne peux pas en avoir deux
Stream
sur un; cela n'a pas de sens - comment feriez-vous une itération sur l'un sans avoir besoin de générer l'autre en même temps? Un flux ne peut être exploité qu'une seule fois.Cependant, si vous voulez les vider dans une liste ou quelque chose, vous pouvez faire
la source
stream.collect(...)
for avec thread-safe prédéfiniCollectors
, qui fonctionne bien même sur des collections non thread-safe (sans conflit de verrouillage synchronisé). Meilleure réponse par @MarkJeronimus.Un collecteur peut être utilisé pour cela.
Collectors.partitioningBy()
usine.Cela créera un
Map
deBoolean
àList
et placera les éléments dans l'une ou l'autre liste basée sur unPredicate
.Remarque: puisque le flux doit être consommé dans son intégralité, cela ne peut pas fonctionner sur des flux infinis. Et comme le flux est de toute façon consommé, cette méthode les place simplement dans des listes au lieu de créer un nouveau flux avec mémoire. Vous pouvez toujours diffuser ces listes si vous avez besoin de flux en sortie.
De plus, pas besoin de l'itérateur, même pas dans l'exemple de tête uniquement que vous avez fourni.
Collectors.groupingBy()
usine.Dans le cas où les flux ne le sont pas
Stream
, mais l'un des flux primitifs commeIntStream
, alors cette.collect(Collectors)
méthode n'est pas disponible. Vous devrez le faire de manière manuelle sans usine de collecte. Sa mise en œuvre ressemble à ceci:[Exemple 2.0 depuis le 16/04/2020]
Dans cet exemple, j'initialise les ArrayLists avec la taille totale de la collection initiale (si cela est connu du tout). Cela empêche les événements de redimensionnement même dans le pire des cas, mais peut potentiellement engloutir 2 * N * T d'espace (N = nombre initial d'éléments, T = nombre de threads). Pour faire un compromis entre l'espace et la vitesse, vous pouvez le laisser de côté ou utiliser votre meilleure estimation éclairée, comme le plus grand nombre d'éléments attendus dans une partition (généralement un peu plus de N / 2 pour une répartition équilibrée).
J'espère que je n'offense personne en utilisant une méthode Java 9. Pour la version Java 8, consultez l'historique des modifications.
la source
stream.boxed().collect(...);
! Il fera comme annoncé: convertir la primitiveIntStream
enStream<Integer>
version boîte .(map, x) -> { boolean partition = p.test(x); List<Integer> list = map.get(partition); list.add(x); }
vous pouvez simplement utiliser(map, x) -> map.get(p.test(x)).add(x)
. De plus, je ne vois aucune raison pour laquelle l'collect
opération ne devrait pas être thread-safe. Cela fonctionne exactement comme il est censé fonctionner et très étroitement à la façon dontCollectors.partitioningBy(p)
cela fonctionnerait. Mais j'utiliserais unIntPredicate
au lieu dePredicate<Integer>
lorsque je ne l'utilise pasboxed()
, pour éviter de boxer deux fois.Je suis tombé sur cette question à moi-même et je pense qu'un flux fourchu a des cas d'utilisation qui pourraient s'avérer valides. J'ai écrit le code ci-dessous en tant que consommateur afin qu'il ne fasse rien mais que vous puissiez l'appliquer à des fonctions et à tout ce que vous pourriez rencontrer.
Maintenant, votre implémentation de code pourrait être quelque chose comme ceci:
la source
Malheureusement, ce que vous demandez est directement mal vu dans le JavaDoc de Stream :
Vous pouvez contourner ce
peek
problème en utilisant ou d'autres méthodes si vous désirez vraiment ce type de comportement. Dans ce cas, ce que vous devriez faire est au lieu d'essayer de sauvegarder deux flux de la même source de flux d'origine avec un filtre de forking, vous dupliqueriez votre flux et filtreriez chacun des doublons de manière appropriée.Cependant, vous souhaiterez peut-être reconsidérer si a
Stream
est la structure appropriée pour votre cas d'utilisation.la source
List<Stream> forkStream(Stream s)
mais mes flux résultants seront au moins partiellement sauvegardés par des collections et non directement par le flux sous-jacent, au lieu de direfilter
qui n'est pas une opération de flux terminal.Cela va à l'encontre du mécanisme général de Stream. Supposons que vous puissiez diviser le Stream S0 en Sa et Sb comme vous le souhaitez. Toute opération de terminal, par exemple
count()
, sur Sa "consommera" nécessairement tous les éléments de S0. Par conséquent, Sb a perdu sa source de données.Auparavant, Stream avait une
tee()
méthode, je pense, qui dupliquait un flux à deux. Il est maintenant supprimé.Stream a cependant une méthode peek (), vous pourrez peut-être l'utiliser pour répondre à vos besoins.
la source
peek
est exactement ce qui étaittee
.pas exactement, mais vous pourrez peut-être accomplir ce dont vous avez besoin en invoquant
Collectors.groupingBy()
. vous créez une nouvelle collection et pouvez ensuite instancier des flux sur cette nouvelle collection.la source
C'était la moins mauvaise réponse que je puisse trouver.
Cela prend un flux d'entiers et les divise en 5. Pour ceux supérieurs à 5, il ne filtre que les nombres pairs et les met dans une liste. Pour le reste, il les rejoint avec |.
les sorties:
Ce n'est pas idéal car il rassemble tout dans des collections intermédiaires brisant le flux (et a trop d'arguments!)
la source
Je suis tombé sur cette question en cherchant un moyen de filtrer certains éléments d'un flux et de les enregistrer comme des erreurs. Je n'avais donc pas vraiment besoin de diviser le flux autant que d'attacher une action de fin prématurée à un prédicat avec une syntaxe discrète. Voici ce que j'ai proposé:
la source
Version plus courte qui utilise Lombok
la source
Que diriez-vous:
la source