Ordine inverso flusso Java 8


153

Domanda generale: qual è il modo corretto di invertire uno stream? Supponendo che non sappiamo in che tipo di elementi è costituito lo stream, qual è il modo generico per invertire qualsiasi stream?

Domanda specifica:

IntStreamfornisce un metodo di intervallo per generare numeri interi in un intervallo specifico IntStream.range(-range, 0), ora che voglio invertirlo cambiando intervallo da 0 a negativo non funzionerà, inoltre non posso usareInteger::compare

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

con IntStreamotterrò questo errore del compilatore

Errore: (191, 0) ajc: il metodo sorted()nel tipo IntStreamnon è applicabile per gli argomenti ( Integer::compare)

cosa mi sto perdendo qui?


1
An IntStreamnon ha .sorted(Comparator)metodo; si deve passare attraverso una Stream<Integer>prima e retromarcia non prima di cedere unIntStream
fge

Ok, l'ho trovato, devi usare .boxed () e poi .sorted ()
vach

4
Per generare un IntStream.range(0, n)ordine inverso, fai qualcosa di simile map(i -> n - i - 1). Non c'è bisogno di fare boxe e smistamento.
Stuart Marks

1
La tua domanda gernerale e la tua domanda specifica mi sembrano due domande completamente diverse. Il generale parla di invertire il flusso , mentre lo specifico parla di ordinare i numeri in ordine decrescente. Se lo stream produce i numeri in modo non ordinato come 1, 3, 2, qual è il risultato atteso? Vuoi il flusso inverso come 2, 3, 1o il flusso ordinato come 3, 2, 1?
Chiccodoro,

8
Non è possibile invertire uno stream in generale, ad esempio uno stream potrebbe essere inifinito.
assylias,

Risposte:


78

Per la domanda specifica di generare un contrario IntStream, prova qualcosa del genere:

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

Ciò evita la boxe e l'ordinamento.

Per la domanda generale su come invertire uno stream di qualsiasi tipo, non so se esiste un modo "corretto". Ci sono un paio di modi a cui riesco a pensare. Entrambi finiscono per memorizzare gli elementi del flusso. Non conosco un modo per invertire un flusso senza memorizzare gli elementi.

Questo primo modo memorizza gli elementi in un array e li legge in un flusso in ordine inverso. Si noti che poiché non conosciamo il tipo di runtime degli elementi stream, non possiamo digitare l'array correttamente, richiedendo un cast non controllato.

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

Un'altra tecnica utilizza i collezionisti per accumulare gli oggetti in un elenco invertito. Questo fa molti inserimenti nella parte anteriore degli ArrayListoggetti, quindi ci sono molte copie in corso.

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

Probabilmente è possibile scrivere un raccoglitore d'inversione molto più efficiente usando una sorta di struttura dati personalizzata.

AGGIORNAMENTO 2016-01-29

Dato che questa domanda ha attirato un po 'di attenzione di recente, immagino che dovrei aggiornare la mia risposta per risolvere il problema con l'inserimento in primo piano ArrayList. Questo sarà orribilmente inefficiente con un gran numero di elementi, che richiederà la copia di O (N ^ 2).

È preferibile utilizzare ArrayDequeinvece un , che supporta in modo efficiente l'inserimento nella parte anteriore. Una piccola ruga è che non possiamo usare la forma a tre argomenti di Stream.collect(); richiede che i contenuti del secondo arg siano uniti nel primo arg, e non ci sono operazioni di massa "add-all-at-front" su Deque. Invece, usiamo addAll()per aggiungere il contenuto del primo argomento alla fine del secondo, e quindi restituiamo il secondo. Ciò richiede l'utilizzo del Collector.of()metodo di fabbrica.

Il codice completo è questo:

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

Il risultato è Dequeinvece di a List, ma non dovrebbe essere un grosso problema, in quanto può essere facilmente ripetuto o riprodotto in streaming nell'ordine ora invertito.


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

2
@Holger Sfortunatamente, quella soluzione non gestisce correttamente l'overflow.
Brandon,

2
@Brandon Mintern: in effetti, dovresti .limit(endExcl-(long)startIncl)invece usarlo , ma per flussi così grandi, è comunque molto scoraggiato in quanto è molto meno efficiente della rangesoluzione basata. Al momento in cui ho scritto il commento, non ero a conoscenza della differenza di efficienza.
Holger,

48

Soluzione elegante

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
È elegante ma non completamente funzionante in quanto sembra che gli elementi dell'elenco Comparable
debbano

26
Ciò presuppone che vogliamo che gli elementi vengano ordinati in ordine inverso. La domanda riguarda l'inversione dell'ordine di un flusso.
Dillon Ryan Redding,

37

Molte delle soluzioni qui ordinano o annullano IntStream, ma ciò richiede inutilmente una memoria intermedia. La soluzione di Stuart Marks è la strada da percorrere:

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

Gestisce correttamente anche l'overflow, superando questo 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});
}

fantastico e semplice. Questo proviene da qualche utilità opensource? (la cosa degli Estreams) o qualche pezzo del tuo codice?
Vach

Grazie! Sfortunatamente, non intendevo perdere il Estreamsnome (lo rimuoverò dal post). È una delle classi di utilità interne della nostra azienda, che utilizziamo per integrare java.util.stream.Streami staticmetodi.
Brandon,

3
Okay ... "fantastico e semplice" ... C'è qualche caso che questa soluzione gestisce, che la soluzione ancora più semplice di Stuart Marks non ha già gestito più di un anno e mezzo fa?
Holger,

Ho appena testato la sua soluzione con il mio metodo di prova sopra; Passa. Stavo evitando inutilmente il trabocco invece di abbracciarlo come ha fatto lui. Sono d'accordo che il suo sia migliore. Modificherò il mio per riflettere questo.
Brandon,

1
@vach, puoi usare StreamExspecificando il passaggio:IntStreamEx.rangeClosed(from-1, to, -1)
Tagir Valeev

34

Domanda generale:

Stream non memorizza alcun elemento.

Quindi non è possibile ripetere gli elementi nell'ordine inverso senza memorizzarli in una raccolta intermedia.

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

Aggiornamento: LinkedList modificato in ArrayDeque (meglio) vedere qui per i dettagli

stampe:

3

20

2

1

A proposito, l'utilizzo del sortmetodo non è corretto in quanto ordina, NON inverte (supponendo che lo stream possa contenere elementi non ordinati)

Domanda specifica:

Ho trovato questo semplice, più facile e intuitivo ( commento @Holger copiato )

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

3
Alcune operazioni flusso, ad esempio sorteded distincteffettivamente memorizzare un risultato intermedio. Consulta i documenti API del pacchetto per alcune informazioni al riguardo.
Lii,

@Lii che vedo No storagenella stessa pagina. Anche se memorizza non possiamo accedere a tale archiviazione (quindi No storageva bene immagino)
Venkata Raju,

Buona risposta. Ma poiché viene utilizzato spazio extra, molti non sono una grande idea per i programmatori di utilizzare il tuo approccio su raccolte molto grandi.
Manu Manjunath,

Mi piace la semplicità della soluzione dal punto di vista della comprensibilità e sfruttando un metodo esistente su una struttura di dati esistente ... la soluzione precedente con un'implementazione della mappa è più difficile da capire, ma sicuramente Manu ha ragione, per le grandi collezioni non lo userei intuitivo, e opterei per la mappa sopra.
Beezer,

Questa è una delle poche risposte corrette qui. La maggior parte degli altri in realtà non inverte lo stream, cerca di evitare di farlo in qualche modo (che funziona solo in particolari circostanze in cui normalmente non è necessario invertire in primo luogo). Se provi a invertire un flusso che non si adatta alla memoria, lo stai facendo comunque in modo sbagliato. Dump in un DB e ottenere un flusso inverso utilizzando SQL normale.
Cubico

19

senza lib esterno ...

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

Se attuata Comparable<T>(es. Integer, String, Date), È possibile farlo utilizzando Comparator.reverseOrder().

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

6
Questo non inverte il flusso. Ordina lo stream in ordine inverso. Quindi se tu avessi Stream.of(1,3,2)il risultato Stream.of(3,2,1)NON sarebbeStream.of(2,3,1)
Wilmol il

12

È possibile definire il proprio raccoglitore che raccoglie gli elementi in ordine inverso:

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

E usalo come:

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

Uso una ArrayList in ordine avanzato per inserire in modo efficiente raccogliere gli elementi (alla fine dell'elenco) e Guava Lists.reverse per fornire in modo efficiente una vista invertita dell'elenco senza crearne un'altra copia.

Ecco alcuni casi di test per il raccoglitore personalizzato:

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- reazioni StreamUtils ha un metodo Stream inverso ( javadoc ).

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

Funziona raccogliendo in una ArrayList e quindi facendo uso della classe ListIterator che può iterare in entrambe le direzioni, per scorrere indietro sull'elenco.

Se hai già un elenco, sarà più efficiente

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

1
Nice non sapeva di questo progetto
vach

1
cyclops ora include anche Spliterator per un'inversione efficiente dello Stream (attualmente per Ranges, array ed Elenchi). La creazione dell'estensione SequenceM Stream di Cyclops con SequenceM.of, SequenceM.range o SequenceM.fromList sfrutterà automaticamente gli splitteratori reversibili in modo efficiente.
John McClean,

6

Suggerirei di usare jOOλ , è un'ottima libreria che aggiunge molte utili funzionalità ai flussi Java 8 e lambdas.

È quindi possibile effettuare le seguenti operazioni:

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

Semplice come quella. È una libreria piuttosto leggera e vale la pena aggiungerla a qualsiasi progetto Java 8.


5

Ecco la soluzione che ho escogitato:

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

quindi usando quei comparatori:

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

2
Questa è solo una risposta alla tua "domanda specifica" ma non alla tua "domanda generale".
Chiccodoro,

9
Collections.reverseOrder()esiste da Java 1.2 e funziona con Integer...
Holger,

4

Che ne dici di questo metodo di utilità?

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

Sembra funzionare con tutti i casi senza duplicazione.


Mi piace molto questa soluzione. La maggior parte delle risposte si divide in due categorie: (1) Invertire la raccolta e .stream () esso, (2) Appello ai collezionisti personalizzati. Entrambi sono assolutamente inutili. Altrimenti, avrebbe testimoniato su un serio problema di espressività del linguaggio nello stesso JDK 8. E la tua risposta dimostra il contrario :)
vitrums

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

3

Il modo più semplice (raccolta semplice - supporta flussi paralleli):

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

Modo avanzato (supporta flussi paralleli in modo continuo):

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

Nota che puoi estendere rapidamente ad altri tipi di flussi (IntStream, ...).

test:

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

risultati:

Six
Five
Four
Three
Two
One

Note aggiuntive: Il simplest waynon è così utile quando viene utilizzato con altre operazioni di flusso (la raccolta si uniscono rompe il parallelismo). Questo advance waynon ha questo problema e mantiene anche le caratteristiche iniziali dello stream, ad esempio SORTED, e quindi è il modo di usarlo con altre operazioni dello stream dopo il contrario.


2

Si potrebbe scrivere un collezionista che raccoglie elementi in ordine inverso:

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

E usalo in questo modo:

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

Risposta originale (contiene un bug - non funziona correttamente per flussi paralleli):

Un metodo di inversione del flusso per scopi generici potrebbe apparire come:

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

2

Non puramente Java8 ma se si utilizza congiuntamente il metodo Lists.reverse () di guava, è possibile ottenere facilmente questo risultato:

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

2

Per quanto riguarda la questione specifica della generazione di un contrario IntStream:

a partire da Java 9 è possibile utilizzare la versione a tre argomenti di 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

dove:

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

  • seed - l'elemento iniziale;
  • hasNext - un predicato da applicare agli elementi per determinare quando deve terminare il flusso;
  • next - una funzione da applicare all'elemento precedente per produrre un nuovo elemento.

1

Per riferimento stavo guardando lo stesso problema, volevo unire il valore di stringa degli elementi stream nell'ordine inverso.

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

Ho iniziato a utilizzare una raccolta intermedia con collectingAndThenda comonad o il ArrayDequecollezionista di Stuart Marks , anche se non ero contento della raccolta intermedia e lo streaming di nuovo

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

Così ho ripetuto la risposta di Stuart Marks che stava usando la Collector.offabbrica, che ha l'interessante lambda della stazione di finitura .

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

Poiché in questo caso il flusso non è parallelo, il combinatore non è così rilevante, lo sto usando insertcomunque per motivi di coerenza del codice, ma non importa in quanto dipende da quale costruttore di stringhe viene creato per primo.

Ho guardato StringJoiner, tuttavia non ha un insertmetodo.


1

Rispondere alla domanda specifica di inversione con IntStream, di seguito ha funzionato per me:

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

1

ArrayDequesono più veloci nello stack rispetto a uno Stack o LinkedList. "push ()" inserisce elementi nella parte anteriore della Deque

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

1

Stringa di inversione o qualsiasi matrice

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

la divisione può essere modificata in base al delimitatore o allo spazio


1

la soluzione più semplice sta usando List::listIteratoreStream::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);

Vale la pena aggiungere che si Stream.generate()genera in un flusso infinito, quindi la chiamata a limit()è molto importante qui.
andrebrait,

0

Questo è come lo faccio.

Non mi piace l'idea di creare una nuova collezione e di invertirla.

L'idea della mappa IntStream # è piuttosto ordinata, ma preferisco il metodo iterato IntStream #, poiché penso che l'idea di un conto alla rovescia per Zero sia meglio espressa con il metodo iterato e più facile da capire in termini di camminata dell'array da dietro a davanti.

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

Ecco alcuni test per dimostrare che funziona:

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 tutto ciò non vedo la risposta a cui vorrei andare per primo.

Questa non è esattamente una risposta diretta alla domanda, ma è una potenziale soluzione al problema.

Costruisci l'elenco all'indietro in primo luogo. Se puoi, usa una LinkedList invece di una ArrayList e quando aggiungi elementi usa "Push" invece di aggiungere. L'elenco verrà creato nell'ordine inverso e quindi verrà riprodotto in streaming correttamente senza alcuna manipolazione.

Questo non si adatta ai casi in cui hai a che fare con matrici o elenchi primitivi che sono già utilizzati in vari modi ma funzionano bene in un numero sorprendente di casi.


0

Questo metodo funziona con qualsiasi stream ed è conforme a Java 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

Il modo più generico e più semplice per invertire un elenco sarà:

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

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

    }

4
Violi il contratto di Comparator. Di conseguenza, nessuno può garantirvi che questo "trucco" funzionerà in qualsiasi futura versione Java con qualsiasi algoritmo di ordinamento. Lo stesso trucco non funziona per il flusso parallelo, ad esempio, come l'algoritmo di ordinamento parallelo utilizza Comparatorin modo diverso. Per l'ordinamento sequenziale funziona solo per caso. Non consiglierei a nessuno di usare questa soluzione.
Tagir Valeev,

1
Inoltre non funziona quando si impostaSystem.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Tagir Valeev il

Ho pensato che si trattasse solo di stampare le cose in ordine inverso, non in ordine ordinato (cioè in ordine decrescente), e funziona anche con flusso parallelo: public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
parmeshwor11

1
Prova reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))(il risultato può dipendere dal numero di core però).
Tagir Valeev,

-1

Java 8 modo per fare questo:

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

4
Si tratta di ordinare in ordine inverso, non di ripristinare un elenco.
Jochen Bedersdorfer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.