Java 8 Stream umgekehrte Reihenfolge


153

Allgemeine Frage: Was ist der richtige Weg, um einen Stream umzukehren? Angenommen, wir wissen nicht, aus welcher Art von Elementen dieser Stream besteht, wie kann ein Stream generisch umgekehrt werden?

Spezifische Frage:

IntStreamBietet eine Bereichsmethode zum Generieren von Ganzzahlen in einem bestimmten Bereich IntStream.range(-range, 0). Jetzt, da ich den Umkehrbereich von 0 auf negativ umkehren möchte, funktioniert dies nicht mehr. Ich kann ihn auch nicht verwendenInteger::compare

List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);

mit IntStreambekomme ich diesen compilerfehler

Fehler: (191, 0) ajc: Die Methode sorted()im Typ IntStreamgilt nicht für die Argumente ( Integer::compare)

Was vermisse ich hier?


1
An IntStreamhat keine .sorted(Comparator)Methode; Sie müssen eine durch Stream<Integer>dort erste und umgekehrt eine vor nachgebendIntStream
fge

Ok, ich habe es gefunden, Sie müssen .boxed () und dann .sorted ()
vach

4
Um eine IntStream.range(0, n)in umgekehrter Reihenfolge zu generieren , gehen Sie wie folgt vor map(i -> n - i - 1). Boxen und Sortieren ist nicht erforderlich.
Stuart Marks

1
Ihre allgemeine Frage und Ihre spezifische Frage lesen sich für mich wie zwei völlig unterschiedliche Fragen. Der General spricht von der Umkehrung des Stroms , während der Spezifische von der Reihenfolge der Nummern in absteigender Reihenfolge spricht. 1, 3, 2Was ist Ihr erwartetes Ergebnis, wenn der Stream die Zahlen auf ungeordnete Weise erzeugt ? Möchten Sie den umgekehrten Stream wie 2, 3, 1oder den sortierten Stream wie 3, 2, 1?
Chiccodoro

8
Sie können einen Stream im Allgemeinen nicht umkehren - zum Beispiel kann ein Stream unendlich sein.
Assylias

Antworten:


78

IntStreamVersuchen Sie für die spezielle Frage, eine Umkehrung zu generieren , Folgendes:

static IntStream revRange(int from, int to) {
    return IntStream.range(from, to)
                    .map(i -> to - i + from - 1);
}

Dies vermeidet Boxen und Sortieren.

Bei der allgemeinen Frage, wie ein Stream eines beliebigen Typs umgekehrt werden kann, weiß ich nicht, dass es einen "richtigen" Weg gibt. Ich kann mir ein paar Möglichkeiten vorstellen. Beide speichern am Ende die Stream-Elemente. Ich kenne keine Möglichkeit, einen Stream umzukehren, ohne die Elemente zu speichern.

Auf diese erste Weise werden die Elemente in einem Array gespeichert und in umgekehrter Reihenfolge in einen Stream ausgelesen. Da wir den Laufzeittyp der Stream-Elemente nicht kennen, können wir das Array nicht richtig eingeben, was eine ungeprüfte Umwandlung erfordert.

@SuppressWarnings("unchecked")
static <T> Stream<T> reverse(Stream<T> input) {
    Object[] temp = input.toArray();
    return (Stream<T>) IntStream.range(0, temp.length)
                                .mapToObj(i -> temp[temp.length - i - 1]);
}

Bei einer anderen Technik werden die Elemente mithilfe von Sammlern in einer umgekehrten Liste zusammengefasst. Dadurch werden viele Einfügungen an der Vorderseite von ArrayListObjekten vorgenommen, sodass viel kopiert wird.

Stream<T> input = ... ;
List<T> output =
    input.collect(ArrayList::new,
                  (list, e) -> list.add(0, e),
                  (list1, list2) -> list1.addAll(0, list2));

Es ist wahrscheinlich möglich, einen viel effizienteren Umkehrkollektor mit einer angepassten Datenstruktur zu schreiben.

UPDATE 29.01.2016

Da diese Frage in letzter Zeit etwas Beachtung gefunden hat, sollte ich meine Antwort aktualisieren, um das Problem mit dem Einfügen vorne zu lösen ArrayList. Dies ist bei einer großen Anzahl von Elementen schrecklich ineffizient und erfordert das Kopieren von O (N ^ 2).

Es ist vorzuziehen, ArrayDequestattdessen ein zu verwenden, das das Einsetzen an der Vorderseite effizient unterstützt. Eine kleine Falte ist, dass wir die Drei-Argumente-Form von nicht verwenden können Stream.collect(); Es erfordert, dass der Inhalt des zweiten Args mit dem ersten Arg zusammengeführt wird, und es gibt keine Massenoperation "Alles an der Vorderseite hinzufügen" Deque. Stattdessen addAll()hängen wir den Inhalt des ersten Arguments an das Ende des zweiten an und geben dann das zweite zurück. Dies erfordert die Verwendung der Collector.of()Factory-Methode.

Der vollständige Code lautet:

Deque<String> output =
    input.collect(Collector.of(
        ArrayDeque::new,
        (deq, t) -> deq.addFirst(t),
        (d1, d2) -> { d2.addAll(d1); return d2; }));

Das Ergebnis ist ein Dequestatt eines List, aber das sollte kein großes Problem sein, da es leicht in der jetzt umgekehrten Reihenfolge iteriert oder gestreamt werden kann.


15
Alternativ:IntStream.iterate(to-1, i->i-1).limit(to-from)
Holger

2
@Holger Leider behandelt diese Lösung den Überlauf nicht richtig.
Brandon

2
@Brandon Mintern: In der Tat müssten Sie .limit(endExcl-(long)startIncl)stattdessen verwenden, aber für solch große Streams wird es sowieso sehr entmutigt, da es viel weniger effizient ist als die rangebasierte Lösung. Als ich den Kommentar schrieb, war mir der Effizienzunterschied nicht bewusst.
Holger

48

Elegante Lösung

List<Integer> list = Arrays.asList(1,2,3,4);
list.stream()
    .boxed() // Converts Intstream to Stream<Integer>
    .sorted(Collections.reverseOrder()) // Method on Stream<Integer>
    .forEach(System.out::println);

6
Es ist elegant, aber nicht voll funktionsfähig, da es so aussieht, als müssten Elemente der Liste Comparable...
Krzysztof Wolny

25
Dies setzt voraus, dass die Elemente in umgekehrter Reihenfolge sortiert werden. Die Frage ist, die Reihenfolge eines Streams umzukehren.
Dillon Ryan Redding

37

Viele der Lösungen hier sortieren oder kehren das um IntStream, aber das erfordert unnötig Zwischenspeicher. Die Lösung von Stuart Marks ist der richtige Weg:

static IntStream revRange(int from, int to) {
    return IntStream.range(from, to).map(i -> to - i + from - 1);
}

Es behandelt auch den Überlauf korrekt und besteht diesen Test:

@Test
public void testRevRange() {
    assertArrayEquals(revRange(0, 5).toArray(), new int[]{4, 3, 2, 1, 0});
    assertArrayEquals(revRange(-5, 0).toArray(), new int[]{-1, -2, -3, -4, -5});
    assertArrayEquals(revRange(1, 4).toArray(), new int[]{3, 2, 1});
    assertArrayEquals(revRange(0, 0).toArray(), new int[0]);
    assertArrayEquals(revRange(0, -1).toArray(), new int[0]);
    assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE).toArray(), new int[0]);
    assertArrayEquals(revRange(MAX_VALUE, MAX_VALUE).toArray(), new int[0]);
    assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE + 1).toArray(), new int[]{MIN_VALUE});
    assertArrayEquals(revRange(MAX_VALUE - 1, MAX_VALUE).toArray(), new int[]{MAX_VALUE - 1});
}

super und einfach. Ist dies von einem OpenSource-Dienstprogramm? (die Estreams-Sache) oder ein Teil Ihres Codes?
Vach

Vielen Dank! Leider hatte ich nicht vor, den EstreamsNamen zu veröffentlichen (ich werde ihn aus dem Beitrag entfernen). Es ist eines unserer unternehmensinternen Utility - Klassen, die wir ergänzen verwenden java.util.stream.Stream‚s staticMethoden.
Brandon

3
Okay… „großartig und einfach“… Gibt es einen Fall, den diese Lösung handhabt, den Stuart Marks 'noch einfachere Lösung vor mehr als eineinhalb Jahren noch nicht bewältigt hat?
Holger

Ich habe gerade seine Lösung mit meiner obigen Testmethode getestet. es geht vorbei. Ich habe unnötigerweise einen Überlauf vermieden, anstatt ihn so anzunehmen, wie er es tat. Ich stimme zu, dass sein besser ist. Ich werde meine bearbeiten, um das zu reflektieren.
Brandon

1
@ Vach, können Sie verwenden, StreamExindem Sie Schritt IntStreamEx.rangeClosed(from-1, to, -1)
angeben

34

Allgemeine Frage:

Stream speichert keine Elemente.

Das Iterieren von Elementen in umgekehrter Reihenfolge ist daher nicht möglich, ohne die Elemente in einer Zwischensammlung zu speichern.

Stream.of("1", "2", "20", "3")
      .collect(Collectors.toCollection(ArrayDeque::new)) // or LinkedList
      .descendingIterator()
      .forEachRemaining(System.out::println);

Update: LinkedList in ArrayDeque geändert (besser) siehe hier für Details

Drucke:

3

20

2

1

Übrigens ist die Verwendung der sortMethode nicht korrekt, da sie sortiert und NICHT umgekehrt wird (vorausgesetzt, der Stream enthält möglicherweise ungeordnete Elemente).

Spezifische Frage:

Ich fand das einfach, einfacher und intuitiver (kopierter @ Holger- Kommentar )

IntStream.iterate(to - 1, i -> i - 1).limit(to - from)

3
Einige Stream-Operationen, wie z. B. sortedund distinctspeichern tatsächlich ein Zwischenergebnis. Informationen dazu finden Sie in den Paket-API-Dokumenten .
Lii

@Lii sehe ich No storageauf der gleichen Seite. Sogar es speichert, dass wir keinen Zugriff auf diesen Speicher bekommen können (also No storageist es in Ordnung, denke ich)
Venkata Raju

Gute Antwort. Da jedoch zusätzlicher Speicherplatz verwendet wird, ist es für Programmierer keine gute Idee, Ihren Ansatz für sehr große Sammlungen zu verwenden.
Manu Manjunath

Ich mag die Einfachheit der Lösung aus Sicht der Verständlichkeit und die Nutzung einer vorhandenen Methode für eine vorhandene Datenstruktur. Die vorherige Lösung mit einer Kartenimplementierung ist schwieriger zu verstehen, aber Manu hat mit Sicherheit Recht, für große Sammlungen würde ich dies nicht verwenden intuitiv und würde sich für die Karte oben entscheiden.
Beezer

Dies ist eine der wenigen richtigen Antworten hier. Die meisten anderen kehren den Stream nicht um, sondern versuchen, dies irgendwie zu vermeiden (was nur unter bestimmten Umständen funktioniert, unter denen Sie normalerweise gar nicht erst umkehren müssen). Wenn Sie versuchen, einen Stream umzukehren, der nicht in den Speicher passt, machen Sie es trotzdem falsch. Speichern Sie es in einer Datenbank und erhalten Sie mithilfe von regulärem SQL einen umgekehrten Stream.
Cubic

19

ohne externe lib ...

import java.util.List;
import java.util.Collections;
import java.util.stream.Collector;

public class MyCollectors {

    public static <T> Collector<T, ?, List<T>> toListReversed() {
        return Collectors.collectingAndThen(Collectors.toList(), l -> {
            Collections.reverse(l);
            return l;
        });
    }

}

15

Wenn umgesetzt Comparable<T>(ex. Integer, String, Date), Können Sie es mit tun Comparator.reverseOrder().

List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.stream()
     .sorted(Comparator.reverseOrder())
     .forEach(System.out::println);

6
Dies kehrt den Stream nicht um. Der Stream wird in umgekehrter Reihenfolge sortiert. Also, wenn Sie Stream.of(1,3,2)das Ergebnis hätten, wäre Stream.of(3,2,1)NICHTStream.of(2,3,1)
Wilmol

12

Sie können Ihren eigenen Kollektor definieren, der die Elemente in umgekehrter Reihenfolge sammelt:

public static <T> Collector<T, List<T>, List<T>> inReverse() {
    return Collector.of(
        ArrayList::new,
        (l, t) -> l.add(t),
        (l, r) -> {l.addAll(r); return l;},
        Lists::<T>reverse);
}

Und benutze es wie:

stream.collect(inReverse()).forEach(t -> ...)

Ich verwende eine ArrayList in Vorwärtsreihenfolge, um die Elemente (am Ende der Liste) effizient einzufügen, und Guava Lists.reverse, um eine effiziente umgekehrte Ansicht der Liste zu erstellen, ohne eine weitere Kopie davon zu erstellen.

Hier sind einige Testfälle für den benutzerdefinierten Sammler:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

import org.hamcrest.Matchers;
import org.junit.Test;

import com.google.common.collect.Lists;

public class TestReverseCollector {
    private final Object t1 = new Object();
    private final Object t2 = new Object();
    private final Object t3 = new Object();
    private final Object t4 = new Object();

    private final Collector<Object, List<Object>, List<Object>> inReverse = inReverse();
    private final Supplier<List<Object>> supplier = inReverse.supplier();
    private final BiConsumer<List<Object>, Object> accumulator = inReverse.accumulator();
    private final Function<List<Object>, List<Object>> finisher = inReverse.finisher();
    private final BinaryOperator<List<Object>> combiner = inReverse.combiner();

    @Test public void associative() {
        final List<Object> a1 = supplier.get();
        accumulator.accept(a1, t1);
        accumulator.accept(a1, t2);
        final List<Object> r1 = finisher.apply(a1);

        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        final List<Object> a3 = supplier.get();
        accumulator.accept(a3, t2);
        final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));

        assertThat(r1, Matchers.equalTo(r2));
    }

    @Test public void identity() {
        final List<Object> a1 = supplier.get();
        accumulator.accept(a1, t1);
        accumulator.accept(a1, t2);
        final List<Object> r1 = finisher.apply(a1);

        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        accumulator.accept(a2, t2);
        final List<Object> r2 = finisher.apply(combiner.apply(a2, supplier.get()));

        assertThat(r1, equalTo(r2));
    }

    @Test public void reversing() throws Exception {
        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        accumulator.accept(a2, t2);

        final List<Object> a3 = supplier.get();
        accumulator.accept(a3, t3);
        accumulator.accept(a3, t4);

        final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));

        assertThat(r2, contains(t4, t3, t2, t1));
    }

    public static <T> Collector<T, List<T>, List<T>> inReverse() {
        return Collector.of(
            ArrayList::new,
            (l, t) -> l.add(t),
            (l, r) -> {l.addAll(r); return l;},
            Lists::<T>reverse);
    }
}

9

CyclU - React StreamUtils verfügt über eine Reverse-Stream-Methode ( Javadoc ).

  StreamUtils.reverse(Stream.of("1", "2", "20", "3"))
             .forEach(System.out::println);

Es funktioniert, indem es in einer ArrayList gesammelt und dann die ListIterator-Klasse verwendet wird, die in beide Richtungen iterieren kann, um rückwärts über die Liste zu iterieren.

Wenn Sie bereits eine Liste haben, ist diese effizienter

  StreamUtils.reversedStream(Arrays.asList("1", "2", "20", "3"))
             .forEach(System.out::println);

1
Nizza wusste nicht über dieses Projekt
vach

1
Cyclops wird jetzt auch mit Spliteratoren für eine effiziente Stream-Umkehrung geliefert (derzeit für Bereiche, Arrays und Listen). Durch das Erstellen der Cyclops SequenceM Stream-Erweiterung mit SequenceM.of, SequenceM.range oder SequenceM.fromList werden automatisch effizient umkehrbare Spliteratoren genutzt.
John McClean

6

Ich würde vorschlagen, jOOλ zu verwenden . Es ist eine großartige Bibliothek, die Java 8-Streams und Lambdas viele nützliche Funktionen hinzufügt.

Sie können dann Folgendes tun:

List<Integer> list = Arrays.asList(1,2,3,4);    
Seq.seq(list).reverse().forEach(System.out::println)

So einfach ist das. Es ist eine ziemlich leichte Bibliothek und es lohnt sich, sie zu jedem Java 8-Projekt hinzuzufügen.


5

Hier ist die Lösung, die ich mir ausgedacht habe:

private static final Comparator<Integer> BY_ASCENDING_ORDER = Integer::compare;
private static final Comparator<Integer> BY_DESCENDING_ORDER = BY_ASCENDING_ORDER.reversed();

dann mit diesen Komparatoren:

IntStream.range(-range, 0).boxed().sorted(BY_DESCENDING_ORDER).forEach(// etc...

2
Dies ist nur eine Antwort auf Ihre "spezifische Frage", nicht aber auf Ihre "allgemeine Frage".
Chiccodoro

9
Collections.reverseOrder()existiert seit Java 1.2 und arbeitet mit Integer
Holger

4

Wie wäre es mit dieser Dienstprogrammmethode?

public static <T> Stream<T> getReverseStream(List<T> list) {
    final ListIterator<T> listIt = list.listIterator(list.size());
    final Iterator<T> reverseIterator = new Iterator<T>() {
        @Override
        public boolean hasNext() {
            return listIt.hasPrevious();
        }

        @Override
        public T next() {
            return listIt.previous();
        }
    };
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
            reverseIterator,
            Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}

Scheint mit allen Fällen ohne Duplizierung zu arbeiten.


Ich mag diese Lösung sehr. Die meisten Antworten sind in zwei Kategorien unterteilt: (1) Die Sammlung umkehren und .stream (), (2) Benutzerdefinierte Sammler ansprechen. Beides ist absolut unnötig. Andernfalls hätte es in JDK 8 selbst ein ernstes Problem mit der Ausdruckskraft der Sprache bezeugt. Und Ihre Antwort beweist das Gegenteil :)
vitrums

4
List newStream = list.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList());
        newStream.forEach(System.out::println);

3

Einfachster Weg (einfaches Sammeln - unterstützt parallele Streams):

public static <T> Stream<T> reverse(Stream<T> stream) {
    return stream
            .collect(Collector.of(
                    () -> new ArrayDeque<T>(),
                    ArrayDeque::addFirst,
                    (q1, q2) -> { q2.addAll(q1); return q2; })
            )
            .stream();
}

Erweiterte Methode (unterstützt fortlaufend parallele Streams):

public static <T> Stream<T> reverse(Stream<T> stream) {
    Objects.requireNonNull(stream, "stream");

    class ReverseSpliterator implements Spliterator<T> {
        private Spliterator<T> spliterator;
        private final Deque<T> deque = new ArrayDeque<>();

        private ReverseSpliterator(Spliterator<T> spliterator) {
            this.spliterator = spliterator;
        }

        @Override
        @SuppressWarnings({"StatementWithEmptyBody"})
        public boolean tryAdvance(Consumer<? super T> action) {
            while(spliterator.tryAdvance(deque::addFirst));
            if(!deque.isEmpty()) {
                action.accept(deque.remove());
                return true;
            }
            return false;
        }

        @Override
        public Spliterator<T> trySplit() {
            // After traveling started the spliterator don't contain elements!
            Spliterator<T> prev = spliterator.trySplit();
            if(prev == null) {
                return null;
            }

            Spliterator<T> me = spliterator;
            spliterator = prev;
            return new ReverseSpliterator(me);
        }

        @Override
        public long estimateSize() {
            return spliterator.estimateSize();
        }

        @Override
        public int characteristics() {
            return spliterator.characteristics();
        }

        @Override
        public Comparator<? super T> getComparator() {
            Comparator<? super T> comparator = spliterator.getComparator();
            return (comparator != null) ? comparator.reversed() : null;
        }

        @Override
        public void forEachRemaining(Consumer<? super T> action) {
            // Ensure that tryAdvance is called at least once
            if(!deque.isEmpty() || tryAdvance(action)) {
                deque.forEach(action);
            }
        }
    }

    return StreamSupport.stream(new ReverseSpliterator(stream.spliterator()), stream.isParallel());
}

Beachten Sie, dass Sie schnell auf andere Arten von Streams (IntStream, ...) erweitern können.

Testen:

// Use parallel if you wish only
revert(Stream.of("One", "Two", "Three", "Four", "Five", "Six").parallel())
    .forEachOrdered(System.out::println);

Ergebnisse:

Six
Five
Four
Three
Two
One

Zusätzliche Hinweise: Das simplest wayist nicht so nützlich, wenn es mit anderen Stream-Operationen verwendet wird (der Collect Join unterbricht die Parallelität). Das advance wayhat dieses Problem nicht, und es behält zum Beispiel auch die anfänglichen Eigenschaften des Streams bei SORTED, und so ist es der Weg, um nach dem Umkehren mit anderen Stream-Operationen zu arbeiten.


2

Man könnte einen Kollektor schreiben, der Elemente in umgekehrter Reihenfolge sammelt:

public static <T> Collector<T, ?, Stream<T>> reversed() {
    return Collectors.collectingAndThen(Collectors.toList(), list -> {
        Collections.reverse(list);
        return list.stream();
    });
}

Und benutze es so:

Stream.of(1, 2, 3, 4, 5).collect(reversed()).forEach(System.out::println);

Ursprüngliche Antwort (enthält einen Fehler - funktioniert nicht ordnungsgemäß für parallele Streams):

Eine Allzweck-Stream-Reverse-Methode könnte folgendermaßen aussehen:

public static <T> Stream<T> reverse(Stream<T> stream) {
    LinkedList<T> stack = new LinkedList<>();
    stream.forEach(stack::push);
    return stack.stream();
}

2

Nicht nur Java8, aber wenn Sie die Lists.reverse () -Methode von guava in Verbindung verwenden, können Sie dies leicht erreichen:

List<Integer> list = Arrays.asList(1,2,3,4);
Lists.reverse(list).stream().forEach(System.out::println);

2

In Bezug auf die spezifische Frage der Erzeugung einer Umkehrung IntStream:

Ab Java 9 können Sie die Version mit drei Argumenten verwenden für IntStream.iterate(...):

IntStream.iterate(10, x -> x >= 0, x -> x - 1).forEach(System.out::println);

// Out: 10 9 8 7 6 5 4 3 2 1 0

wo:

IntStream.iterate​(int seed, IntPredicate hasNext, IntUnaryOperator next);

  • seed - das Anfangselement;
  • hasNext - ein Prädikat, das auf Elemente angewendet werden soll, um zu bestimmen, wann der Stream beendet werden muss;
  • next - Eine Funktion, die auf das vorherige Element angewendet wird, um ein neues Element zu erzeugen.

1

Als Referenz habe ich mir das gleiche Problem angesehen, ich wollte den String-Wert von Stream-Elementen in umgekehrter Reihenfolge verbinden.

itemList = {last, middle, first} => first, middle, last

Ich fing an, eine Zwischensammlung mit collectingAndThenvon comonad oder dem ArrayDequeSammler von Stuart Marks zu verwenden , obwohl ich mit der Zwischensammlung nicht zufrieden war und wieder Streaming

itemList.stream()
        .map(TheObject::toString)
        .collect(Collectors.collectingAndThen(Collectors.toList(),
                                              strings -> {
                                                      Collections.reverse(strings);
                                                      return strings;
                                              }))
        .stream()
        .collect(Collector.joining());

Also habe ich die Antwort von Stuart Marks wiederholt, die die Collector.ofFabrik benutzte , die den interessanten Finisher Lambda hat.

itemList.stream()
        .collect(Collector.of(StringBuilder::new,
                             (sb, o) -> sb.insert(0, o),
                             (r1, r2) -> { r1.insert(0, r2); return r1; },
                             StringBuilder::toString));

Da in diesem Fall der Stream nicht parallel ist, ist der Kombinierer nicht so relevant, wie ich verwende insert aus Gründen der Codekonsistenz trotzdem, aber es spielt keine Rolle, da dies davon abhängt, welcher Stringbuilder zuerst erstellt wird.

Ich habe mir den StringJoiner angesehen, aber er hat keine insertMethode.


1

Die Beantwortung einer bestimmten Frage zum Rückgängigmachen mit IntStream hat für mich funktioniert:

IntStream.range(0, 10)
  .map(x -> x * -1)
  .sorted()
  .map(Math::abs)
  .forEach(System.out::println);

1

ArrayDequesind schneller im Stapel als ein Stapel oder eine verknüpfte Liste. "push ()" fügt Elemente an der Vorderseite des Deque ein

 protected <T> Stream<T> reverse(Stream<T> stream) {
    ArrayDeque<T> stack = new ArrayDeque<>();
    stream.forEach(stack::push);
    return stack.stream();
}

1

Zeichenfolge oder Array umkehren

(Stream.of("abcdefghijklm 1234567".split("")).collect(Collectors.collectingAndThen(Collectors.toList(),list -> {Collections.reverse(list);return list;}))).stream().forEach(System.out::println);

Die Aufteilung kann basierend auf dem Trennzeichen oder Leerzeichen geändert werden


1

Die einfachste Lösung ist die Verwendung von List::listIteratorundStream::generate

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
ListIterator<Integer> listIterator = list.listIterator(list.size());

Stream.generate(listIterator::previous)
      .limit(list.size())
      .forEach(System.out::println);

Es lohnt sich hinzuzufügen, dass Stream.generate()in unendlichen Stream generiert wird, daher ist der Aufruf von limit()hier sehr wichtig.
andrebrait

0

So mache ich es.

Ich mag die Idee nicht, eine neue Sammlung zu erstellen und sie rückgängig zu machen.

Die IntStream # -Kartenidee ist ziemlich ordentlich, aber ich bevorzuge die IntStream # -Iterationsmethode, da ich denke, dass die Idee eines Countdowns bis Null mit der Iterationsmethode besser ausgedrückt und leichter zu verstehen ist, wenn das Array von hinten nach vorne verschoben wird.

import static java.lang.Math.max;

private static final double EXACT_MATCH = 0d;

public static IntStream reverseStream(final int[] array) {
    return countdownFrom(array.length - 1).map(index -> array[index]);
}

public static DoubleStream reverseStream(final double[] array) {
    return countdownFrom(array.length - 1).mapToDouble(index -> array[index]);
}

public static <T> Stream<T> reverseStream(final T[] array) {
    return countdownFrom(array.length - 1).mapToObj(index -> array[index]);
}

public static IntStream countdownFrom(final int top) {
    return IntStream.iterate(top, t -> t - 1).limit(max(0, (long) top + 1));
}

Hier sind einige Tests, um zu beweisen, dass es funktioniert:

import static java.lang.Integer.MAX_VALUE;
import static org.junit.Assert.*;

@Test
public void testReverseStream_emptyArrayCreatesEmptyStream() {
    Assert.assertEquals(0, reverseStream(new double[0]).count());
}

@Test
public void testReverseStream_singleElementCreatesSingleElementStream() {
    Assert.assertEquals(1, reverseStream(new double[1]).count());
    final double[] singleElementArray = new double[] { 123.4 };
    assertArrayEquals(singleElementArray, reverseStream(singleElementArray).toArray(), EXACT_MATCH);
}

@Test
public void testReverseStream_multipleElementsAreStreamedInReversedOrder() {
    final double[] arr = new double[] { 1d, 2d, 3d };
    final double[] revArr = new double[] { 3d, 2d, 1d };
    Assert.assertEquals(arr.length, reverseStream(arr).count());
    Assert.assertArrayEquals(revArr, reverseStream(arr).toArray(), EXACT_MATCH);
}

@Test
public void testCountdownFrom_returnsAllElementsFromTopToZeroInReverseOrder() {
    assertArrayEquals(new int[] { 4, 3, 2, 1, 0 }, countdownFrom(4).toArray());
}

@Test
public void testCountdownFrom_countingDownStartingWithZeroOutputsTheNumberZero() {
    assertArrayEquals(new int[] { 0 }, countdownFrom(0).toArray());
}

@Test
public void testCountdownFrom_doesNotChokeOnIntegerMaxValue() {
    assertEquals(true, countdownFrom(MAX_VALUE).anyMatch(x -> x == MAX_VALUE));
}

@Test
public void testCountdownFrom_givesZeroLengthCountForNegativeValues() {
    assertArrayEquals(new int[0], countdownFrom(-1).toArray());
    assertArrayEquals(new int[0], countdownFrom(-4).toArray());
}

0

In all dem sehe ich nicht die Antwort, zu der ich zuerst gehen würde.

Dies ist nicht gerade eine direkte Antwort auf die Frage, aber es ist eine mögliche Lösung für das Problem.

Erstellen Sie die Liste zunächst rückwärts. Wenn Sie können, verwenden Sie eine LinkedList anstelle einer ArrayList. Wenn Sie Elemente hinzufügen, verwenden Sie "Push" anstelle von "Hinzufügen". Die Liste wird in umgekehrter Reihenfolge erstellt und dann ohne Manipulation korrekt gestreamt.

Dies passt nicht zu Fällen, in denen Sie mit primitiven Arrays oder Listen arbeiten, die bereits auf verschiedene Weise verwendet werden, aber in überraschend vielen Fällen gut funktionieren.


0

Diese Methode funktioniert mit jedem Stream und ist Java 8-kompatibel:

Stream<Integer> myStream = Stream.of(1, 2, 3, 4, 5);
myStream.reduce(Stream.empty(),
        (Stream<Integer> a, Integer b) -> Stream.concat(Stream.of(b), a),
        (a, b) -> Stream.concat(b, a))
        .forEach(System.out::println);

-1

Der allgemeinste und einfachste Weg, eine Liste umzukehren, ist:

public static <T> void reverseHelper(List<T> li){

 li.stream()
.sorted((x,y)-> -1)
.collect(Collectors.toList())
.forEach(System.out::println);

    }

4
Sie verletzen den Vertrag von Comparator. Daher kann Ihnen niemand garantieren, dass dieser "Trick" in jeder zukünftigen Java-Version mit einem beliebigen Sortieralgorithmus funktioniert. Der gleiche Trick funktioniert beispielsweise nicht für parallele Streams, da der parallele Sortieralgorithmus Comparatorauf unterschiedliche Weise verwendet wird. Bei der sequentiellen Sortierung funktioniert dies rein zufällig. Ich würde niemandem empfehlen, diese Lösung zu verwenden.
Tagir Valeev

1
Auch funktioniert es nicht, wenn Sie einstellenSystem.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Tagir Valeev

Ich dachte, es geht nur darum, Dinge in umgekehrter Reihenfolge zu drucken, nicht in sortierter Reihenfolge (dh absteigender Reihenfolge), und es funktioniert auch mit parallelem Stream: public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
parmeshwor11

1
Versuchen Sie es reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))(das Ergebnis kann jedoch von der Anzahl der Kerne abhängen).
Tagir Valeev

-1

Java 8 Weg, dies zu tun:

    List<Integer> list = Arrays.asList(1,2,3,4);
    Comparator<Integer> comparator = Integer::compare;
    list.stream().sorted(comparator.reversed()).forEach(System.out::println);

4
Dies erfolgt in umgekehrter Reihenfolge, ohne dass eine Liste zurückgesetzt wird.
Jochen Bedersdorfer
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.