514 Stimmen

Gibt es eine prägnante Möglichkeit, über einen Stream mit Indizes in Java 8 zu iterieren?

Gibt es einen prägnanten Weg, über einen Stream zu iterieren und dabei Zugriff auf den Index im Stream zu haben?

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

List nameList;
Stream 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());

was im Vergleich zum dort gegebenen LINQ-Beispiel recht enttäuschend erscheint

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

Gibt es einen prägnanteren Weg?

Zudem scheint es, dass zip entweder verschoben oder entfernt wurde...

3voto

Donald Raab Punkte 6053

Wenn Sie keine Bedenken haben, eine Fremdbibliothek zu verwenden, hat Eclipse Collections zipWithIndex und forEachWithIndex verfügbar für viele Typen. Hier ist eine Reihe von Lösungen für diese Herausforderung für JDK-Typen und Eclipse Collections-Typen, die zipWithIndex verwenden.

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

// JDK-Typen
List strings1 = ArrayIterate.zipWithIndex(names)
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings1);

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

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

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

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

Hier ist eine Lösung, die stattdessen forEachWithIndex verwendet.

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

List 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 Mitwirkender für Eclipse Collections

3voto

Samuel Philipp Punkte 9993

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

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List 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 und höher. In Java 8 können Sie dies verwenden:

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

3voto

42n4 Punkte 1254

Mit https://github.com/poetix/protonpack können Sie das zippen:

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

List nameList;
Stream 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);

3voto

Josh M Punkte 10993

Es gibt keine Möglichkeit, über einen Stream zu iterieren und gleichzeitig auf den Index zuzugreifen, da ein Stream anders als eine Collection ist. Ein Stream ist lediglich eine Pipeline zur Weiterleitung von Daten von einem Ort zum anderen, wie es in der Dokumentation erläutert wird:

Kein Speicher. Ein Stream ist keine Datenstruktur, die Elemente speichert; stattdessen transportieren sie Werte von einer Quelle (die eine Datenstruktur, ein Generator, ein IO-Kanal, etc. sein könnte) durch eine Abfolge von Berechnungsoperationen.

Natürlich könnten Sie, wie Sie in Ihrer Frage anzudeuten scheinen, Ihren Stream immer in eine Collection, wie z.B. eine List, umwandeln, in der Sie Zugriff auf die Indizes haben.

2voto

live-love Punkte 40586

Wenn Sie einen Index basierend auf einem Prädikat erhalten möchten, versuchen Sie es mit:

Wenn es Ihnen nur um den ersten Index geht:

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

Oder wenn Sie mehrere Indizes finden möchten:

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

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X