Gibt es eine präzise Möglichkeit, einen Stream mit Indizes in Java 8 zu durchlaufen?

382

Gibt es eine übersichtliche Möglichkeit, einen Stream zu durchlaufen, während Sie Zugriff auf den Index im Stream haben?

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());

Das scheint ziemlich enttäuschend im Vergleich zu dem dort gegebenen LINQ-Beispiel

string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();

Gibt es einen prägnanteren Weg?

Weiter scheint es, dass der Reißverschluss entweder bewegt oder entfernt wurde ...

Graeme Moss
quelle
2
Was ist intRange()? Ich bin bisher noch nicht auf diese Methode in Java 8 gestoßen.
Rohit Jain
@RohitJain Wahrscheinlich ein IntStream.rangeClosed(x, y).
Assylias
2
Als List<String> allCities = map.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList());
Nebenkommentar
3
Ja, zipwurde entfernt, zusammen mit experimentellen zweiwertigen Streams, die unterschiedlich als BiStreamoder bezeichnet wurden MapStream. Das Hauptproblem besteht darin, dass Java, um dies effektiv zu tun, wirklich einen strukturell typisierten Paartyp (oder Tupeltyp) benötigt. Fehlt eine, ist es einfach, eine generische Paar- oder Tupelklasse zu erstellen - dies wurde schon oft durchgeführt -, aber alle werden auf den gleichen Typ gelöscht.
Stuart Marks
3
Ein weiteres Problem mit einer generischen Pair- oder Tuple-Klasse besteht darin, dass alle Grundelemente eingerahmt werden müssen.
Stuart Marks

Antworten:

435

Der sauberste Weg ist, von einem Strom von Indizes auszugehen:

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
IntStream.range(0, names.length)
         .filter(i -> names[i].length() <= i)
         .mapToObj(i -> names[i])
         .collect(Collectors.toList());

Die resultierende Liste enthält nur "Erik".


Eine Alternative , die besser vertraut aussieht , wenn Sie verwendet werden , um für Schleifen würden ein Ad - hoc - Zähler unter Verwendung eines veränderliches Objekt zu halten, zum Beispiel ein AtomicInteger:

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
AtomicInteger index = new AtomicInteger();
List<String> list = Arrays.stream(names)
                          .filter(n -> n.length() <= index.incrementAndGet())
                          .collect(Collectors.toList());

Beachten Sie, dass die Verwendung der letzteren Methode in einem parallelen Stream unterbrochen werden kann, da die Elemente nicht unbedingt "in der richtigen Reihenfolge" verarbeitet werden müssen .

Assylien
quelle
28
Die Verwendung von Atomics auf diese Weise ist bei parallelen Streams problematisch. Erstens muss die Reihenfolge der Verarbeitung von Elementen nicht unbedingt mit der Reihenfolge übereinstimmen, in der Elemente im anfänglichen Array vorkommen. Daher stimmt der mit dem Atom zugewiesene "Index" wahrscheinlich nicht mit dem tatsächlichen Array-Index überein. Zweitens, während Atomics threadsicher sind, kann es zu Konflikten zwischen mehreren Threads kommen, die das Atom aktualisieren, wodurch die Parallelität verringert wird.
Stuart Marks
1
Ich habe eine ähnliche Lösung wie @assylias entwickelt. Um das erwähnte Problem mit dem parallelen Stream @StuartMarks zu umgehen, mache ich zuerst einen bestimmten parallelen Stream sequentiell, führe die Zuordnung durch und stelle den parallelen Zustand wieder her. 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); } }
Daniel Dietrich
4
@ DanielDietrich Wenn du denkst, dass es die Frage löst, solltest du es als Antwort und nicht als Kommentar posten (und der Code wird auch besser lesbar sein!).
Assylias
3
@ DanielDietrich Entschuldigung, wenn ich diesen Code richtig lese, funktioniert er nicht. Es können nicht verschiedene Segmente einer Pipeline parallel oder sequentiell ausgeführt werden. Nur der letzte von paralleloder sequentialwird geehrt, wenn der Terminalbetrieb beginnt.
Stuart Marks
4
Aus Gründen der Gerechtigkeit wurde "der sauberste Weg" aus @ Stuarts Antwort gestohlen.
Vadzim
70

Der Java 8-Streams-API fehlen die Funktionen zum Abrufen des Index eines Stream-Elements sowie die Möglichkeit, Streams zusammen zu komprimieren. Dies ist bedauerlich, da dadurch bestimmte Anwendungen (wie die LINQ-Herausforderungen) schwieriger werden als sonst.

Es gibt jedoch häufig Problemumgehungen. Normalerweise kann dies erreicht werden, indem der Stream mit einem ganzzahligen Bereich "angesteuert" wird und die Tatsache ausgenutzt wird, dass sich die ursprünglichen Elemente häufig in einem Array oder in einer Sammlung befinden, auf die über den Index zugegriffen werden kann. Zum Beispiel kann das Challenge 2-Problem folgendermaßen gelöst werden:

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};

List<String> nameList =
    IntStream.range(0, names.length)
        .filter(i -> names[i].length() <= i)
        .mapToObj(i -> names[i])
        .collect(toList());

Wie oben erwähnt, nutzt dies die Tatsache aus, dass die Datenquelle (das Namensarray) direkt indizierbar ist. Wenn es nicht wäre, würde diese Technik nicht funktionieren.

Ich gebe zu, dass dies nicht die Absicht von Herausforderung 2 erfüllt. Trotzdem löst es das Problem einigermaßen effektiv.

BEARBEITEN

In meinem vorherigen Codebeispiel wurden flatMapdie Filter- und Kartenoperationen zusammengeführt, dies war jedoch umständlich und bot keinen Vorteil. Ich habe das Beispiel gemäß dem Kommentar von Holger aktualisiert.

Stuart Marks
quelle
7
Wie wäre es mit IntStream.range(0, names.length).filter(i->names[i].length()<=i).mapToObj(i->names[i])? Es funktioniert ohne Boxen…
Holger
1
Hm, ja, warum dachte ich, ich müsste es flatMaptrotzdem benutzen ?
Stuart Marks
2
Zum Schluss noch einmal ... Ich habe es wahrscheinlich verwendet, flatMapweil es eine Filter- und Zuordnungsoperation zu einer einzigen Operation zusammenführt, aber das bietet wirklich keinen Vorteil. Ich werde das Beispiel bearbeiten.
Stuart Marks
Stream.of (Array) erstellt eine Stream-Schnittstelle für ein Array. Effektiv Stream.of( names ).filter( n -> n.length() <= 1).collect( Collectors.toList() );weniger Unboxing und weniger Speicherzuweisung; da wir keinen Range Stream mehr erstellen.
Code Eyez
44

Seit Guave 21 können Sie verwenden

Streams.mapWithIndex()

Beispiel (aus dem offiziellen Dokument ):

Streams.mapWithIndex(
    Stream.of("a", "b", "c"),
    (str, index) -> str + ":" + index)
) // will return Stream.of("a:0", "b:1", "c:2")
numéro6
quelle
3
Außerdem haben die Guava-Leute forEachWithIndex noch nicht implementiert (wobei ein Verbraucher anstelle einer Funktion verwendet wird), aber es handelt sich um ein zugewiesenes Problem: github.com/google/guava/issues/2913 .
John Glassmyer
25

Ich habe in meinem Projekt die folgende Lösung verwendet. Ich denke, es ist besser als veränderbare Objekte oder ganzzahlige Bereiche zu verwenden.

import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.util.Objects.requireNonNull;


public class CollectionUtils {
    private CollectionUtils() { }

    /**
     * Converts an {@link java.util.Iterator} to {@link java.util.stream.Stream}.
     */
    public static <T> Stream<T> iterate(Iterator<? extends T> iterator) {
        int characteristics = Spliterator.ORDERED | Spliterator.IMMUTABLE;
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, characteristics), false);
    }

    /**
     * Zips the specified stream with its indices.
     */
    public static <T> Stream<Map.Entry<Integer, T>> zipWithIndex(Stream<? extends T> stream) {
        return iterate(new Iterator<Map.Entry<Integer, T>>() {
            private final Iterator<? extends T> streamIterator = stream.iterator();
            private int index = 0;

            @Override
            public boolean hasNext() {
                return streamIterator.hasNext();
            }

            @Override
            public Map.Entry<Integer, T> next() {
                return new AbstractMap.SimpleImmutableEntry<>(index++, streamIterator.next());
            }
        });
    }

    /**
     * Returns a stream consisting of the results of applying the given two-arguments function to the elements of this stream.
     * The first argument of the function is the element index and the second one - the element value. 
     */
    public static <T, R> Stream<R> mapWithIndex(Stream<? extends T> stream, BiFunction<Integer, ? super T, ? extends R> mapper) {
        return zipWithIndex(stream).map(entry -> mapper.apply(entry.getKey(), entry.getValue()));
    }

    public static void main(String[] args) {
        String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};

        System.out.println("Test zipWithIndex");
        zipWithIndex(Arrays.stream(names)).forEach(entry -> System.out.println(entry));

        System.out.println();
        System.out.println("Test mapWithIndex");
        mapWithIndex(Arrays.stream(names), (Integer index, String name) -> index+"="+name).forEach((String s) -> System.out.println(s));
    }
}
user1195526
quelle
+1 - konnte eine Funktion implementieren, die ein Element, das alle N Indizes verwendet, StreamSupport.stream()und einen benutzerdefinierten Iterator "einfügt" .
Ach
13

Zusätzlich zum Protonpack bietet Seq von jOOλ diese Funktionalität (und durch Erweiterungsbibliotheken, die darauf aufbauen, wie Zyklopen reagieren , bin ich der Autor dieser Bibliothek).

Seq.seq(Stream.of(names)).zipWithIndex()
                         .filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
                         .toList();

Seq unterstützt auch nur Seq.of (Namen) und erstellt einen JDK-Stream unter der Decke.

Das einfach reagierende Äquivalent würde ähnlich aussehen

 LazyFutureStream.of(names)
                 .zipWithIndex()
                 .filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
                 .toList();

Die einfach reagierende Version ist eher auf asynchrone / gleichzeitige Verarbeitung zugeschnitten.

John McClean
quelle
John, ich habe heute deine Bibliothek gesehen. Ich bin sowohl erstaunt als auch verwirrt.
GOXR3PLUS
12

Der Vollständigkeit halber ist hier die Lösung für meine StreamEx- Bibliothek:

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
EntryStream.of(names)
    .filterKeyValue((idx, str) -> str.length() <= idx+1)
    .values().toList();

Hier erstellen wir eine, EntryStream<Integer, String>die Stream<Entry<Integer, String>>einige spezifische Operationen wie filterKeyValueoder erweitert und hinzufügt values. Es wird auch eine toList()Verknüpfung verwendet.

Tagir Valeev
quelle
Gute Arbeit; Gibt es eine Abkürzung für .forEach(entry -> {}) ?
Steve Oh
2
@SteveOh wenn ich deine Frage richtig verstehe, dann kannst du ja schreiben .forKeyValue((key, value) -> {}).
Tagir Valeev
8

Ich habe die Lösungen hier gefunden, wenn der Stream aus einer Liste oder einem Array erstellt wird (und Sie die Größe kennen). Aber was ist, wenn Stream eine unbekannte Größe hat? In diesem Fall versuchen Sie diese Variante:

public class WithIndex<T> {
    private int index;
    private T value;

    WithIndex(int index, T value) {
        this.index = index;
        this.value = value;
    }

    public int index() {
        return index;
    }

    public T value() {
        return value;
    }

    @Override
    public String toString() {
        return value + "(" + index + ")";
    }

    public static <T> Function<T, WithIndex<T>> indexed() {
        return new Function<T, WithIndex<T>>() {
            int index = 0;
            @Override
            public WithIndex<T> apply(T t) {
                return new WithIndex<>(index++, t);
            }
        };
    }
}

Verwendungszweck:

public static void main(String[] args) {
    Stream<String> stream = Stream.of("a", "b", "c", "d", "e");
    stream.map(WithIndex.indexed()).forEachOrdered(e -> {
        System.out.println(e.index() + " -> " + e.value());
    });
}
alex.b
quelle
6

Mit einer Liste können Sie versuchen

List<String> strings = new ArrayList<>(Arrays.asList("First", "Second", "Third", "Fourth", "Fifth")); // An example list of Strings
strings.stream() // Turn the list into a Stream
    .collect(HashMap::new, (h, o) -> h.put(h.size(), o), (h, o) -> {}) // Create a map of the index to the object
        .forEach((i, o) -> { // Now we can use a BiConsumer forEach!
            System.out.println(String.format("%d => %s", i, o));
        });

Ausgabe:

0 => First
1 => Second
2 => Third
3 => Fourth
4 => Fifth
V0idst4r
quelle
1
Eigentlich eine nette Idee, aber strings :: indexOf könnte etwas teuer sein. Mein Vorschlag ist, stattdessen zu verwenden: .collect (HashMap :: new, (h, s) -> h.put (h.size (), s), (h, s) -> {}) . Sie können einfach die size () -Methode verwenden, um den Index zu erstellen.
Gil.fernandes
@ gil.fernandes Danke für den Vorschlag. Ich werde die Änderungen vornehmen.
V0idst4r
3

Es gibt keine Möglichkeit, über eine Weile zu iterieren, Streamwährend Sie Zugriff auf den Index haben, da a Streamanders ist als alle anderen Collection. A Streamist lediglich eine Pipeline zum Übertragen von Daten von einem Ort zum anderen, wie in der Dokumentation angegeben :

Kein Speicher. Ein Stream ist keine Datenstruktur, in der Elemente gespeichert werden. Stattdessen übertragen sie Werte von einer Quelle (die eine Datenstruktur, ein Generator, ein E / A-Kanal usw. sein kann) durch eine Pipeline von Rechenoperationen.

Natürlich können Sie, wie Sie in Ihrer Frage anscheinend andeuten, Ihre jederzeit Stream<V>in eine konvertieren Collection<V>, z. B. eine List<V>, in der Sie Zugriff auf die Indizes haben.

Josh M.
quelle
2
Dies ist in anderen Sprachen / Tools verfügbar. Es ist einfach ein inkrementeller Wert, der an die Kartenfunktion übergeben wird
Lee Campbell
Ihr Link zur Dokumentation ist fehlerhaft.
Usman Mutawakil
3

Mit https://github.com/poetix/protonpack können Sie diese Zip-Datei erstellen:

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};

List<String> nameList;
Stream<Integer> indices = IntStream.range(0, names.length).boxed(); 

nameList = StreamUtils.zip(indices, stream(names),SimpleEntry::new)
        .filter(e -> e.getValue().length() <= e.getKey()).map(Entry::getValue).collect(toList());                   

System.out.println(nameList);
42n4
quelle
3

Wenn Sie nichts dagegen haben eine Drittanbieter - Bibliothek, Eclipse - Kollektionen haben zipWithIndexund forEachWithIndexverfügbar für den Einsatz in vielen Arten. Hier finden Sie eine Reihe von Lösungen für diese Herausforderung, die sowohl für JDK-Typen als auch für Eclipse-Sammlungstypen verwendet werden zipWithIndex.

String[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
ImmutableList<String> expected = Lists.immutable.with("Erik");
Predicate<Pair<String, Integer>> predicate =
    pair -> pair.getOne().length() <= pair.getTwo() + 1;

// JDK Types
List<String> strings1 = ArrayIterate.zipWithIndex(names)
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings1);

List<String> list = Arrays.asList(names);
List<String> strings2 = ListAdapter.adapt(list)
    .zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings2);

// Eclipse Collections types
MutableList<String> mutableNames = Lists.mutable.with(names);
MutableList<String> strings3 = mutableNames.zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings3);

ImmutableList<String> immutableNames = Lists.immutable.with(names);
ImmutableList<String> strings4 = immutableNames.zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings4);

MutableList<String> strings5 = mutableNames.asLazy()
    .zipWithIndex()
    .collectIf(predicate, Pair::getOne, Lists.mutable.empty());
Assert.assertEquals(expected, strings5);

Hier ist eine Lösung, die forEachWithIndexstattdessen verwendet wird.

MutableList<String> mutableNames =
    Lists.mutable.with("Sam", "Pamela", "Dave", "Pascal", "Erik");
ImmutableList<String> expected = Lists.immutable.with("Erik");

List<String> actual = Lists.mutable.empty();
mutableNames.forEachWithIndex((name, index) -> {
        if (name.length() <= index + 1)
            actual.add(name);
    });
Assert.assertEquals(expected, actual);

Wenn Sie die Lambdas oben in anonyme innere Klassen ändern, funktionieren alle diese Codebeispiele auch in Java 5 - 7.

Hinweis: Ich bin ein Committer für Eclipse-Sammlungen

Donald Raab
quelle
2

Wenn Sie zufällig Vavr (früher bekannt als Javaslang) verwenden, können Sie die dedizierte Methode nutzen:

Stream.of("A", "B", "C")
  .zipWithIndex();

Wenn wir den Inhalt ausdrucken, werden wir etwas Interessantes sehen:

Stream((A, 0), ?)

Dies liegt daran, dass Streamswir faul sind und keine Ahnung haben, welche Elemente im Stream als nächstes angezeigt werden.

Grzegorz Piwowarek
quelle
1

Wenn Sie versuchen, einen Index basierend auf einem Prädikat zu erhalten, versuchen Sie Folgendes:

Wenn Sie sich nur für den ersten Index interessieren:

OptionalInt index = IntStream.range(0, list.size())
    .filter(i -> list.get(i) == 3)
    .findFirst();

Oder wenn Sie mehrere Indizes suchen möchten:

IntStream.range(0, list.size())
   .filter(i -> list.get(i) == 3)
   .collect(Collectors.toList());

Fügen .orElse(-1);Sie hinzu, falls Sie einen Wert zurückgeben möchten, wenn er nicht gefunden wird.

Live-Liebe
quelle
1

Hier ist Code von AbacusUtil

Stream.of(names).indexed()
      .filter(e -> e.value().length() <= e.index())
      .map(Indexed::value).toList();

Offenlegung: Ich bin der Entwickler von AbacusUtil.

user_3380739
quelle
1

Sie können verwenden IntStream.iterate(), um den Index zu erhalten:

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList = IntStream.iterate(0, i -> i < names.length, i -> i + 1)
        .filter(i -> names[i].length() <= i)
        .mapToObj(i -> names[i])
        .collect(Collectors.toList());

Dies funktioniert nur für Java 9 ab Java 8. Sie können Folgendes verwenden:

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList = IntStream.iterate(0, i -> i + 1)
        .limit(names.length)
        .filter(i -> names[i].length() <= i)
        .mapToObj(i -> names[i])
        .collect(Collectors.toList());
Samuel Philipp
quelle
0

Sie können eine statische innere Klasse erstellen, um den Indexer wie im folgenden Beispiel zu kapseln:

static class Indexer {
    int i = 0;
}

public static String getRegex() {
    EnumSet<MeasureUnit> range = EnumSet.allOf(MeasureUnit.class);
    StringBuilder sb = new StringBuilder();
    Indexer indexer = new Indexer();
    range.stream().forEach(
            measureUnit -> {
                sb.append(measureUnit.acronym);
                if (indexer.i < range.size() - 1)
                    sb.append("|");

                indexer.i++;
            }
    );
    return sb.toString();
}
alexpfx
quelle
0

Diese Frage ( Stream Way zum Abrufen des Index des ersten Elements, das mit dem Booleschen Wert übereinstimmt ) hat die aktuelle Frage als Duplikat markiert, sodass ich sie dort nicht beantworten kann. Ich beantworte es hier.

Hier ist eine generische Lösung, um den passenden Index zu erhalten, für den keine externe Bibliothek erforderlich ist.

Wenn Sie eine Liste haben.

public static <T> int indexOf(List<T> items, Predicate<T> matches) {
        return IntStream.range(0, items.size())
                .filter(index -> matches.test(items.get(index)))
                .findFirst().orElse(-1);
}

Und nenne es so:

int index = indexOf(myList, item->item.getId()==100);

Und wenn Sie eine Sammlung verwenden, probieren Sie diese aus.

   public static <T> int indexOf(Collection<T> items, Predicate<T> matches) {
        int index = -1;
        Iterator<T> it = items.iterator();
        while (it.hasNext()) {
            index++;
            if (matches.test(it.next())) {
                return index;
            }
        }
        return -1;
    }
Steven Spungin
quelle
0

Eine Möglichkeit besteht darin, jedes Element im Flow zu indizieren:

AtomicInteger index = new AtomicInteger();
Stream.of(names)
  .map(e->new Object() { String n=e; public i=index.getAndIncrement(); })
  .filter(o->o.n.length()<=o.i) // or do whatever you want with pairs...
  .forEach(o->System.out.println("idx:"+o.i+" nam:"+o.n));

Die Verwendung einer anonymen Klasse entlang eines Streams wird nicht gut verwendet, ist aber sehr nützlich.

Jean-Baptiste Yunès
quelle
0

Sie brauchen nicht zu einem map notwendigerweise
, die am nächsten Lambda zum LINQ Beispiel ist:

int[] idx = new int[] { 0 };
Stream.of( names ).filter( name -> name.length() <= idx[0]++ ).collect( Collectors.toList() );
Kaplan
quelle
0
String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
String completeString
         =  IntStream.range(0,namesArray.length)
           .mapToObj(i -> namesArray[i]) // Converting each array element into Object
           .map(String::valueOf) // Converting object to String again
           .collect(Collectors.joining(",")); // getting a Concat String of all values
        System.out.println(completeString);

AUSGABE: Sam, Pamela, Dave, Pascal, Erik

String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};

IntStream.range(0,namesArray.length)
               .mapToObj(i -> namesArray[i]) // Converting each array element into Object
               .map(String::valueOf) // Converting object to String again
               .forEach(s -> {
                //You can do various operation on each element here
                System.out.println(s);
               }); // getting a Concat String of all 

So sammeln Sie in der Liste:

String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
 List<String> namesList
                =  IntStream.range(0,namesArray.length)
                .mapToObj(i -> namesArray[i]) // Converting each array element into Object
                .map(String::valueOf) // Converting object to String again
                .collect(Collectors.toList()); // collecting elements in List
        System.out.println(listWithIndex);
Arpan Saini
quelle
Es wird erwartet, dass Listdie Lösung der obigen Frage ein Element enthält, das Erik enthält .
Kaplan
Ich habe ein Beispiel zum Sammeln in der Liste hinzugefügt.
Arpan Saini
0

Wie jean-baptiste-yunès sagte, wenn Ihr Stream auf einer Java-Liste basiert, ist die Verwendung einer AtomicInteger und ihrer incrementAndGet-Methode eine sehr gute Lösung für das Problem, und die zurückgegebene Ganzzahl entspricht dem Index in der ursprünglichen Liste, solange Sie Verwenden Sie keinen parallelen Stream.

Mario
quelle
0

Wenn Sie den Index in forEach benötigen, bietet dies eine Möglichkeit.

  public class IndexedValue {

    private final int    index;
    private final Object value;

    public IndexedValue(final int index, final Object value) { 
        this.index = index;
        this.value = value;
    }

    public int getIndex() {
        return index;
    }

    public Object getValue() {
        return value;
    }
}

Verwenden Sie es dann wie folgt.

@Test
public void withIndex() {
    final List<String> list = Arrays.asList("a", "b");
    IntStream.range(0, list.size())
             .mapToObj(index -> new IndexedValue(index, list.get(index)))
             .forEach(indexValue -> {
                 System.out.println(String.format("%d, %s",
                                                  indexValue.getIndex(),
                                                  indexValue.getValue().toString()));
             });
}
B. Stackhouse
quelle