Odwrotna kolejność strumieni Java 8


153

Pytanie ogólne: Jaki jest właściwy sposób odwrócenia strumienia? Zakładając, że nie wiemy, z jakiego typu elementów składa się strumień, jaki jest ogólny sposób odwrócenia dowolnego strumienia?

Konkretne pytanie:

IntStreamzapewnia metodę zakresu do generowania liczb całkowitych w określonym zakresie IntStream.range(-range, 0), teraz, gdy chcę to odwrócić, przełączanie zakresu od 0 do ujemnego nie zadziała, również nie mogę użyćInteger::compare

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

z IntStreamotrzymam ten błąd kompilatora

Błąd: (191, 0) ajc: Metoda sorted()w typie IntStreamnie ma zastosowania do argumentów ( Integer::compare)

czego tu brakuje?


1
An IntStreamnie ma .sorted(Comparator)metody; musisz przejść przez Stream<Integer>pierwszy i tam zawrócić, zanim IntStream
uzyskasz

Ok, znalazłem to, musisz użyć .boxed (), a następnie .sorted ()
vach

4
Aby wygenerować IntStream.range(0, n)w odwrotnej kolejności, zrób coś takiego map(i -> n - i - 1). Nie musisz robić boksu i sortować.
Stuart Marks

1
Twoje ogólne pytanie i twoje konkretne pytanie brzmią dla mnie jak dwa zupełnie różne pytania. Generał mówi o odwróceniu strumienia , podczas gdy szczegół mówi o porządkowaniu liczb w porządku malejącym. Jeśli strumień generuje liczby w nieuporządkowany sposób 1, 3, 2, jaki jest twój oczekiwany wynik? Czy chcesz, aby strumień był odwrócony, 2, 3, 1czy posortowany 3, 2, 1?
chiccodoro

8
Generalnie nie można odwrócić strumienia - na przykład strumień może być nieskończony.
assylias

Odpowiedzi:


78

Jeśli chodzi o konkretną kwestię generowania rewersu IntStream, spróbuj czegoś takiego:

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

Pozwala to uniknąć boksowania i sortowania.

Jeśli chodzi o ogólne pytanie, jak odwrócić strumień dowolnego typu, nie wiem, czy istnieje „właściwy” sposób. Mogę wymyślić kilka sposobów. Oba kończą się przechowywaniem elementów strumienia. Nie znam sposobu na odwrócenie strumienia bez przechowywania elementów.

Ten pierwszy sposób zapisuje elementy w tablicy i odczytuje je do strumienia w odwrotnej kolejności. Zwróć uwagę, że ponieważ nie znamy typu środowiska wykonawczego elementów strumienia, nie możemy poprawnie wpisać tablicy, co wymaga niezaznaczonego rzutowania.

@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]);
}

Inna technika wykorzystuje kolekcjonerów do gromadzenia elementów w odwróconej liście. To powoduje wiele wstawek z przodu ArrayListobiektów, więc jest dużo kopiowania.

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

Prawdopodobnie jest możliwe napisanie znacznie bardziej wydajnego kolektora wstecznego przy użyciu jakiejś niestandardowej struktury danych.

UPDATE 2016-01-29

Ponieważ ostatnio zwrócono uwagę na to pytanie, myślę, że powinienem zaktualizować swoją odpowiedź, aby rozwiązać problem z wstawianiem z przodu ArrayList. Będzie to okropnie nieefektywne przy dużej liczbie elementów, wymagającym kopiowania O (N ^ 2).

Lepiej jest użyć ArrayDequezamiast tego, który skutecznie wspiera wkładanie z przodu. Mała zmarszczka polega na tym, że nie możemy użyć formy trójargumentowej Stream.collect(); wymaga, aby zawartość drugiego argumentu została scalona z pierwszym argumentem i nie ma żadnej operacji zbiorczej typu „dodaj wszystko z przodu” Deque. Zamiast tego addAll()dodajemy zawartość pierwszego argumentu do końca drugiego, a następnie zwracamy drugi. Wymaga to użycia Collector.of()metody fabrycznej.

Kompletny kod jest następujący:

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

Rezultatem jest a Dequezamiast a List, ale nie powinno to stanowić większego problemu, ponieważ można go łatwo iterować lub przesyłać strumieniowo w odwróconej kolejności.


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

2
@Holger Niestety, to rozwiązanie nie obsługuje poprawnie przepełnienia.
Brandon

2
@Brandon Mintern: rzeczywiście, musiałbyś użyć .limit(endExcl-(long)startIncl)zamiast tego, ale w przypadku tak dużych strumieni jest to i tak bardzo odradzane, ponieważ jest znacznie mniej wydajne niż rangerozwiązanie bazowe . W czasie, gdy pisałem komentarz, nie byłem świadomy różnicy w wydajności.
Holger

48

Eleganckie rozwiązanie

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
Jest elegancki, ale nie do końca działa, jak się wydaje, ponieważ elementy listy muszą być Comparable...
Krzysztof Wolny

26
To przy założeniu, że chcemy, aby elementy były sortowane w odwrotnej kolejności. Pytanie dotyczy odwrócenia kolejności strumienia.
Dillon Ryan Redding

37

Wiele z przedstawionych tutaj rozwiązań sortuje lub odwraca IntStream, ale to niepotrzebnie wymaga pośredniego przechowywania. Rozwiązanie Stuarta Marksa jest do zrobienia:

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

Prawidłowo radzi sobie również z przepełnieniem, przechodząc ten 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});
}

niesamowite i proste. Czy to z jakiegoś narzędzia open source? (sprawa Estreams) czy jakiś fragment twojego kodu?
vach

Dzięki! Niestety nie zamierzałem ujawnić Estreamsnazwy (zamierzam usunąć ją z postu). To jedna z klas użytkowych wewnętrznych naszej firmy, których używamy do uzupełnienia java.util.stream.Stream„s staticmetod.
Brandon,

3
Okej… „niesamowite i proste”… Czy jest jakikolwiek przypadek, że to rozwiązanie obsługuje, z którym nawet prostsze rozwiązanie Stuarta Marksa nie poradziło sobie już ponad półtora roku temu?
Holger

Właśnie przetestowałem jego rozwiązanie za pomocą mojej metody testowej powyżej; to przechodzi. Niepotrzebnie unikałem przepełnienia, zamiast go obejmować, tak jak on. Zgadzam się, że jego jest lepsze. Zmienię moje, aby to odzwierciedlić.
Brandon

1
@vach, możesz użyć, StreamExokreślając krok:IntStreamEx.rangeClosed(from-1, to, -1)
Tagir Valeev

34

Pytanie ogólne:

Stream nie przechowuje żadnych elementów.

Dlatego iterowanie elementów w odwrotnej kolejności nie jest możliwe bez przechowywania elementów w jakiejś kolekcji pośredniej.

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

Aktualizacja: Zmieniono LinkedList na ArrayDeque (lepiej), patrz tutaj, aby uzyskać szczegółowe informacje

Wydruki:

3

20

2

1

Nawiasem mówiąc, użycie sortmetody nie jest poprawne, ponieważ sortuje, NIE odwraca (zakładając, że strumień może mieć nieuporządkowane elementy)

Konkretne pytanie:

Znalazłem to proste, łatwiejsze i intuicyjne (skopiowany komentarz @Holger )

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

3
Niektóre operacje na strumieniu, takie jak sortedi distinctfaktycznie przechowują wynik pośredni. Zobacz dokumentację pakietu API, aby uzyskać więcej informacji na ten temat.
Lii

@Lii widzę No storagena tej samej stronie. Nawet jeśli przechowuje, nie możemy uzyskać dostępu do tego magazynu (więc No storagemyślę, że jest w porządku)
Venkata Raju

Dobra odpowiedź. Ponieważ jednak używana jest dodatkowa przestrzeń, często nie jest dobrym pomysłem dla programistów stosowanie twojego podejścia do bardzo dużej kolekcji.
Manu Manjunath

Podoba mi się prostota rozwiązania z punktu widzenia zrozumiałości i wykorzystanie istniejącej metody na istniejącej strukturze danych ... rozwiązanie poprzednie z implementacją mapy jest trudniejsze do zrozumienia, ale na pewno Manu ma rację, dla dużych kolekcji nie użyłbym tego intuicyjny i zdecydowałby się na powyższą mapę.
Beezer

To jedna z niewielu poprawnych odpowiedzi. Większość innych tak naprawdę nie odwraca strumienia, starają się jakoś tego uniknąć (co działa tylko w szczególnych okolicznościach, w których normalnie nie trzeba by było odwracać w pierwszej kolejności). Jeśli spróbujesz odwrócić strumień, który nie mieści się w pamięci, i tak robisz to źle. Zrzuć go do bazy danych i uzyskaj odwrócony strumień za pomocą zwykłego SQL.
Cubic

19

bez biblioteki zewnętrznej ...

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

Jeśli realizowane Comparable<T>(np. Integer, String, Date), Można to zrobić za pomocą Comparator.reverseOrder().

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

6
To nie odwraca strumienia. Sortuje strumień w odwrotnej kolejności. Więc jeśli miałbyś Stream.of(1,3,2)wynik byłby Stream.of(3,2,1)NIEStream.of(2,3,1)
wilmol

12

Możesz zdefiniować własny kolektor, który zbiera elementy w odwrotnej kolejności:

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

I używaj go jak:

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

Używam ArrayList w celu wydajnego wstawiania zbierania elementów (na końcu listy) i Guava Lists.reverse, aby efektywnie dawać odwrócony widok listy bez wykonywania jej kolejnej kopii.

Oto kilka przypadków testowych dla niestandardowego modułu zbierającego:

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

cyclops- interact StreamUtils ma metodę odwróconego strumienia ( javadoc ).

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

Działa zbierając do ArrayList, a następnie wykorzystując klasę ListIterator, która może iterować w dowolnym kierunku, aby wykonać iterację wstecz po liście.

Jeśli masz już listę, będzie bardziej wydajna

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

1
Miło nie wiedziałem o tym projekcie
vach

1
Cyclops jest teraz również wyposażony w rozdzielacze do wydajnego odwracania strumienia (obecnie dla zakresów, tablic i list). Tworzenie cyklopów Rozszerzenie SequenceM Stream za pomocą SequenceM.of, SequenceM.range lub SequenceM.fromList automatycznie korzysta z wydajnie odwracalnych rozdzielaczy.
John McClean,

6

Sugerowałbym użycie jOOλ , to świetna biblioteka, która dodaje wiele przydatnych funkcji do strumieni Java 8 i lambd.

Następnie możesz wykonać następujące czynności:

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

Proste. Jest to dość lekka biblioteka, którą warto dodać do każdego projektu Java 8.


5

Oto rozwiązanie, które wymyśliłem:

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

następnie używając tych komparatorów:

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

2
To jest tylko odpowiedź na Twoje „konkretne pytanie”, ale nie na Twoje „ogólne pytanie”.
chiccodoro

9
Collections.reverseOrder()istnieje od wersji Java 1.2 i współpracuje z Integer
Holger

4

A co z tą metodą użytkową?

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

Wydaje się działać we wszystkich przypadkach bez powielania.


To rozwiązanie bardzo mi się podoba. Większość odpowiedzi dzieli się na dwie kategorie: (1) Reverse the collection i .stream () it, (2) odwołanie do niestandardowych kolekcjonerów. Oba są absolutnie niepotrzebne. W przeciwnym razie świadczyłby o poważnym problemie z ekspresją języka w samym JDK 8. A twoja odpowiedź dowodzi
czegoś

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

3

Najprostszy sposób (proste zbieranie - obsługuje równoległe strumienie):

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

Zaawansowany sposób (obsługuje strumienie równoległe w sposób ciągły):

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

Zauważ, że możesz szybko rozszerzyć na inne typy strumieni (IntStream, ...).

Testowanie:

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

Wyniki:

Six
Five
Four
Three
Two
One

Dodatkowe uwagi: Nie simplest wayjest to tak przydatne, gdy jest używane z innymi operacjami na strumieniu (łączenie kolekcjonowania przerywa równoległość). advance wayNie ma tego problemu, a także zachowuje pierwotne właściwości strumienia, na przykład SORTED, i tak, jest to droga do użytku z innych operacji strumienia po odwrocie.


2

Można by napisać kolektor, który zbiera elementy w odwrotnej kolejności:

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

I użyj tego w ten sposób:

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

Oryginalna odpowiedź (zawiera błąd - nie działa poprawnie dla równoległych strumieni):

Metoda odwrócenia strumienia ogólnego przeznaczenia może wyglądać następująco:

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

2

Nie tylko Java8, ale jeśli użyjesz metody Lists.reverse () guawy w połączeniu, możesz łatwo osiągnąć to:

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

2

W odniesieniu do konkretnej kwestii generowania rewersu IntStream:

zaczynając od Java 9 możesz użyć trzyargumentowej wersji 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

gdzie:

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

  • seed - element początkowy;
  • hasNext - predykat do zastosowania do elementów w celu określenia, kiedy strumień musi się zakończyć;
  • next - funkcja, która ma być zastosowana do poprzedniego elementu w celu utworzenia nowego elementu.

1

Dla odniesienia przyglądałem się temu samemu problemowi, chciałem połączyć wartość ciągu elementów strumienia w odwrotnej kolejności.

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

Zacząłem używać kolekcji z pośrednią collectingAndThenz comonad lub ArrayDequekolektora Stuart towarowe , chociaż nie byłem zadowolony z kolekcji pośredniej i strumieniowe ponownie

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

Powtórzyłem więc odpowiedź Stuarta Marksa Collector.of, która dotyczyła fabryki, która ma interesującą lambdę finiszera .

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

Ponieważ w tym przypadku strumień nie jest równoległy, sumator nie jest tak bardzo istotny, i tak używam insertze względu na spójność kodu, ale nie ma to znaczenia, ponieważ zależy to od tego, który kompilator ciągów jest zbudowany jako pierwszy.

Spojrzałem na StringJoiner, jednak nie ma on insertmetody.


1

Odpowiedź na konkretne pytanie dotyczące cofania za pomocą IntStream, poniżej działała dla mnie:

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

1

ArrayDequesą szybsze na stosie niż Stack lub LinkedList. „push ()” wstawia elementy z przodu Deque

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

1

Odwracanie ciągu lub dowolnej tablicy

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

podział można modyfikować na podstawie separatora lub spacji


1

najprostszym rozwiązaniem jest użycie List::listIteratoriStream::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);

Warto dodać, że Stream.generate()generuje się w nieskończonym strumieniu, więc wezwanie do limit()jest tutaj bardzo ważne.
andrebrait

0

Tak to robię.

Nie podoba mi się pomysł tworzenia nowej kolekcji i jej odwracania.

Pomysł na mapę IntStream # jest całkiem zgrabny, ale wolę metodę iteracyjną IntStream #, ponieważ myślę, że idea odliczania do zera jest lepiej wyrażona za pomocą metody iteracyjnej i łatwiejsza do zrozumienia pod względem chodzenia po tablicy od tyłu do przodu.

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

Oto kilka testów, które potwierdzają, że to działa:

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

W tym wszystkim nie widzę odpowiedzi, do której bym poszedł pierwszy.

Nie jest to do końca bezpośrednia odpowiedź na pytanie, ale jest to potencjalne rozwiązanie problemu.

Po prostu zbuduj listę od tyłu. Jeśli możesz, użyj LinkedList zamiast ArrayList, a podczas dodawania elementów użyj opcji „Wypchnij” zamiast dodawać. Lista zostanie utworzona w odwrotnej kolejności, a następnie będzie poprawnie przesyłana strumieniowo bez żadnych manipulacji.

Nie będzie to pasowało do przypadków, w których masz do czynienia z prymitywnymi tablicami lub listami, które są już używane na różne sposoby, ale działają dobrze w zaskakującej liczbie przypadków.


0

Ta metoda działa z dowolnym strumieniem i jest zgodna z Javą 8:

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

Najbardziej ogólną i najłatwiejszą metodą odwrócenia listy będzie:

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

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

    }

4
Naruszasz umowę z Comparator. W rezultacie nikt nie może zagwarantować, że ta „sztuczka” zadziała w każdej przyszłej wersji Javy z dowolnym algorytmem sortowania. Ta sama sztuczka nie działa na przykład w przypadku strumienia równoległego, ponieważ algorytm sortowania równoległego wykorzystuje Comparatorw inny sposób. W przypadku sortowania sekwencyjnego działa to wyłącznie przypadkowo. Nie polecałbym nikomu korzystania z tego rozwiązania.
Tagir Valeev

1
Nie działa też, kiedy ustawiaszSystem.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Tagir Valeev

Myślałem, że chodzi o drukowanie rzeczy w odwrotnej kolejności, a nie w kolejności posortowanej (tj. public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
Malejącej

1
Spróbuj reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))(wynik może jednak zależeć od liczby rdzeni).
Tagir Valeev

-1

Java 8, jak to zrobić:

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

4
To jest sortowanie w odwrotnej kolejności, a nie odwracanie listy.
Jochen Bedersdorfer
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.