Existe-t-il un moyen concis d'itérer sur un flux tout en ayant accès à l'index dans le flux?
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList;
Stream<Integer> indices = intRange(1, names.length).boxed();
nameList = zip(indices, stream(names), SimpleEntry::new)
.filter(e -> e.getValue().length() <= e.getKey())
.map(Entry::getValue)
.collect(toList());
ce qui semble plutôt décevant par rapport à l'exemple LINQ qui y est donné
string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();
Existe-t-il un moyen plus concis?
De plus, il semble que le zip a été déplacé ou supprimé ...
java
java-8
java-stream
Graeme Moss
la source
la source
intRange()
? Je n'ai pas rencontré cette méthode dans Java 8 jusqu'à présent.IntStream.rangeClosed(x, y)
.List<String> allCities = map.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList());
zip
été supprimé, ainsi que les flux expérimentaux à deux valeurs, appelésBiStream
ou ouMapStream
. Le problème principal est que pour le faire efficacement, Java a vraiment besoin d'un type de paire (ou tuple) typé structurellement. À défaut, il est facile de créer une classe Pair ou Tuple générique - cela a été fait plusieurs fois - mais ils effacent tous le même type.Réponses:
La façon la plus propre est de partir d'un flux d'indices:
La liste résultante contient uniquement "Erik".
Une alternative qui semble plus familière lorsque vous êtes habitué aux boucles serait de maintenir un compteur ad hoc en utilisant un objet modifiable, par exemple un
AtomicInteger
:Notez que l' utilisation de cette dernière méthode sur un flux parallèle pourrait se casser car les éléments ne seraient pas nécessairement traités "dans l'ordre" .
la source
public static <T> Stream<Tuple2<Integer, T>> zipWithIndex(Stream<T> stream) { final AtomicInteger index = new AtomicInteger(); final Function<T, Tuple2<Integer, T>> zipper = e -> Tuples.of(index.getAndIncrement(), e); if (stream.isParallel()) { return stream.sequential().map(zipper).parallel(); } else { return stream.map(zipper); } }
parallel
ousequential
est honoré lorsque l'opération de terminal commence.L'API de flux Java 8 n'a pas les fonctionnalités permettant d'obtenir l'index d'un élément de flux ainsi que la possibilité de compresser les flux ensemble. C'est regrettable, car cela rend certaines applications (comme les défis LINQ) plus difficiles qu'elles ne le seraient autrement.
Il existe cependant souvent des solutions de contournement. Habituellement, cela peut être fait en "pilotant" le flux avec une plage entière, et en profitant du fait que les éléments d'origine sont souvent dans un tableau ou dans une collection accessible par index. Par exemple, le problème du Challenge 2 peut être résolu de cette façon:
Comme je l'ai mentionné ci-dessus, cela profite du fait que la source de données (le tableau des noms) est directement indexable. Si ce n'était pas le cas, cette technique ne fonctionnerait pas.
J'admets que cela ne répond pas à l'intention du défi 2. Néanmoins, cela résout le problème de manière raisonnablement efficace.
ÉDITER
Mon exemple de code précédent avait l'habitude
flatMap
de fusionner les opérations de filtrage et de mappage, mais c'était lourd et n'offrait aucun avantage. J'ai mis à jour l'exemple par le commentaire de Holger.la source
IntStream.range(0, names.length).filter(i->names[i].length()<=i).mapToObj(i->names[i])
? Ça marche sans boxe…flatMap
toute façon?flatMap
parce qu'il fusionne en quelque sorte une opération de filtrage et de mappage en une seule opération, mais cela ne fournit vraiment aucun avantage. Je vais éditer l'exemple.Stream.of( names ).filter( n -> n.length() <= 1).collect( Collectors.toList() );
moins unboxing et moins d'allocation de mémoire; car nous ne créons plus de flux de plage.Depuis la goyave 21, vous pouvez utiliser
Exemple (extrait du doc officiel ):
la source
J'ai utilisé la solution suivante dans mon projet. Je pense que c'est mieux que d'utiliser des objets mutables ou des plages entières.
la source
StreamSupport.stream()
et un itérateur personnalisé.En plus de protonpack, la Seq de jOOλ fournit cette fonctionnalité (et par extension des bibliothèques qui s'appuient dessus comme cyclops-react , je suis l'auteur de cette bibliothèque).
Seq prend également en charge uniquement Seq.of (noms) et créera un flux JDK sous les couvertures.
L'équivalent à réaction simple ressemblerait à
La version simple-react est plus adaptée au traitement asynchrone / simultané.
la source
Juste pour être complet, voici la solution impliquant ma bibliothèque StreamEx :
Ici, nous créons un
EntryStream<Integer, String>
qui étendStream<Entry<Integer, String>>
et ajoute des opérations spécifiques commefilterKeyValue
ouvalues
. UntoList()
raccourci est également utilisé.la source
.forEach(entry -> {})
?.forKeyValue((key, value) -> {})
.J'ai trouvé les solutions ici lorsque le Stream est créé de liste ou de tableau (et vous connaissez la taille). Mais que se passe-t-il si Stream est de taille inconnue? Dans ce cas, essayez cette variante:
Usage:
la source
Avec une liste, vous pouvez essayer
Production:
la source
Il n'y a aucun moyen d'itérer sur a
Stream
tout en ayant accès à l'index car aStream
ne ressemble à aucunCollection
. AStream
est simplement un pipeline pour transporter des données d'un endroit à un autre, comme indiqué dans la documentation :Pas de stockage. Un flux n'est pas une structure de données qui stocke des éléments; au lieu de cela, ils transportent les valeurs d'une source (qui pourrait être une structure de données, un générateur, un canal d'E / S, etc.) via un pipeline d'opérations de calcul.
Bien sûr, comme vous semblez le laisser entendre dans votre question, vous pouvez toujours convertir votre fichier
Stream<V>
en unCollection<V>
, comme unList<V>
, dans lequel vous aurez accès aux index.la source
Avec https://github.com/poetix/protonpack, vous pouvez faire ce zip:
la source
Si cela ne vous dérange pas d'utiliser une bibliothèque tierce, Eclipse Collections a
zipWithIndex
et estforEachWithIndex
disponible pour une utilisation sur de nombreux types. Voici un ensemble de solutions à ce défi pour les types JDK et les types de collections Eclipse utilisantzipWithIndex
.Voici une solution utilisant à la
forEachWithIndex
place.Si vous modifiez les lambdas en classes internes anonymes ci-dessus, tous ces exemples de code fonctionneront également en Java 5-7.
Remarque: je suis un committer pour les collections Eclipse
la source
Si vous utilisez Vavr (anciennement Javaslang), vous pouvez tirer parti de la méthode dédiée:
Si nous imprimons le contenu, nous verrons quelque chose d'intéressant:
En effet, nous
Streams
sommes paresseux et nous n'avons aucune idée des éléments suivants dans le flux.la source
Si vous essayez d'obtenir un index basé sur un prédicat, essayez ceci:
Si vous ne vous souciez que du premier index:
Ou si vous souhaitez rechercher plusieurs index:
Ajoutez
.orElse(-1);
au cas où vous voudriez retourner une valeur s'il ne la trouve pas.la source
Voici le code d' AbacusUtil
Divulgation : Je suis le développeur d'AbacusUtil.
la source
Vous pouvez utiliser
IntStream.iterate()
pour obtenir l'index:Cela ne fonctionne que pour Java 9 vers le haut dans Java 8, vous pouvez utiliser ceci:
la source
Vous pouvez créer une classe interne statique pour encapsuler l'indexeur comme je devais le faire dans l'exemple ci-dessous:
la source
Cette question ( Stream Way pour obtenir l'indice du premier élément correspondant à un booléen ) a marqué la question actuelle comme un doublon, donc je ne peux pas y répondre; Je réponds ici.
Voici une solution générique pour obtenir l'index correspondant qui ne nécessite pas de bibliothèque externe.
Si vous avez une liste.
Et appelez-le comme ceci:
Et si vous utilisez une collection, essayez celle-ci.
la source
Une façon possible consiste à indexer chaque élément sur le flux:
L'utilisation d'une classe anonyme le long d'un flux n'est pas bien utilisée tout en étant très utile.
la source
vous n'avez pas besoin
map
nécessairementqui est le plus proche lambda à l'exemple de LINQ:
la source
SORTIE: Sam, Pamela, Dave, Pascal, Erik
Pour collecter dans la liste:
la source
List
contenant un élément .Comme l'a dit Jean-Baptiste-Yunès, si votre flux est basé sur une liste Java, l'utilisation d'un AtomicInteger et de sa méthode incrementAndGet est une très bonne solution au problème et l'entier renvoyé correspond à l'index de la liste d'origine tant que vous n'utilisez pas de flux parallèle.
la source
Si vous avez besoin de l'index dans forEach, cela fournit un moyen.
Ensuite, utilisez-le comme suit.
la source