Ordem Reversa do Java 8 Stream

153

Pergunta geral: Qual é a maneira correta de reverter um fluxo? Supondo que não sabemos em que tipo de elementos esse fluxo consiste, qual é a maneira genérica de reverter qualquer fluxo?

Pergunta específica:

IntStreamfornece o método range para gerar números inteiros em um intervalo específico IntStream.range(-range, 0), agora que eu quero revertê-lo, mudar o intervalo de 0 para negativo não funcionará, também não posso usarInteger::compare

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

com IntStreameu vou receber esse erro do compilador

Erro: (191, 0) ajc: o método sorted()no tipo IntStreamnão é aplicável aos argumentos ( Integer::compare)

O que estou perdendo aqui?

vach
fonte
1
Um IntStreamnão tem .sorted(Comparator)método; você tem que passar por um Stream<Integer>primeiro e reverter lá antes de produzir um IntStream
erro
Ok, eu encontrei-o, você precisa usar .boxed () e, em seguida, .sorted ()
Vach
4
Para gerar uma IntStream.range(0, n)ordem inversa, faça algo parecido map(i -> n - i - 1). Não há necessidade de fazer boxe e classificação.
Stuart Marcas
1
Sua pergunta geral e sua pergunta específica parecem duas perguntas diferentes para mim. O general fala em reverter o fluxo , enquanto o específico fala em ordenar números em ordem decrescente. Se o fluxo produz os números de maneira desordenada 1, 3, 2, qual é o resultado esperado? Deseja como o fluxo invertido 2, 3, 1ou como o fluxo classificado 3, 2, 1?
chiccodoro
8
Você não pode reverter um fluxo em geral - por exemplo, um fluxo pode ser inifinito.
assylias

Respostas:

78

Para a questão específica de gerar um reverso IntStream, tente algo como isto:

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

Isso evita boxe e classificação.

Para a questão geral de como reverter um fluxo de qualquer tipo, não sei se existe uma maneira "adequada". Existem algumas maneiras em que posso pensar. Ambos acabam armazenando os elementos do fluxo. Não sei como reverter um fluxo sem armazenar os elementos.

Essa primeira maneira armazena os elementos em uma matriz e os lê em um fluxo na ordem inversa. Observe que, como não sabemos o tipo de tempo de execução dos elementos do fluxo, não podemos digitar a matriz corretamente, exigindo uma conversão não verificada.

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

Outra técnica usa coletores para acumular os itens em uma lista invertida. Isso faz muitas inserções na frente dos ArrayListobjetos, então há muitas cópias acontecendo.

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

Provavelmente, é possível escrever um coletor de reversão muito mais eficiente usando algum tipo de estrutura de dados personalizada.

UPDATE 2016-01-29

Como essa pergunta recebeu um pouco de atenção recentemente, acho que devo atualizar minha resposta para resolver o problema com a inserção na frente ArrayList. Isso será terrivelmente ineficiente com um grande número de elementos, exigindo cópia de O (N ^ 2).

É preferível usar um ArrayDeque, que suporta com eficiência a inserção na frente. Uma pequena ruga é que não podemos usar a forma de três argumentos de Stream.collect(); requer que o conteúdo do segundo argumento seja mesclado com o primeiro argumento e não há operação em massa "adicionar tudo à frente" Deque. Em vez disso, usamos addAll()para anexar o conteúdo do primeiro argumento ao final do segundo e depois retornamos o segundo. Isso requer o uso do Collector.of()método de fábrica.

O código completo é este:

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

O resultado é um em Dequevez de um List, mas isso não deve ser um grande problema, pois pode ser facilmente iterado ou transmitido na ordem agora invertida.

Stuart Marks
fonte
15
Alternativamente:IntStream.iterate(to-1, i->i-1).limit(to-from)
Holger
2
@ Holger Infelizmente, essa solução não lida corretamente com o estouro.
Brandon
2
@Brandon Mintern: na verdade, você teria que usar .limit(endExcl-(long)startIncl), mas para fluxos tão grandes, é muito desencorajado de qualquer maneira, pois é muito menos eficiente que a rangesolução baseada. No momento em que escrevi o comentário, não estava ciente da diferença de eficiência.
Holger
48

Solução 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);
Kishan B
fonte
6
Ele é elegante, mas não totalmente funcional como parece como elementos da lista são obrigados a estar Comparable...
Krzysztof Wolny
26
Isso pressupõe que queremos que os elementos sejam classificados em ordem inversa. A questão é reverter a ordem de um fluxo.
Dillon Ryan Redding #
37

Muitas das soluções aqui classificam ou revertem a IntStream, mas isso requer desnecessariamente armazenamento intermediário. A solução de Stuart Marks é o caminho a seguir:

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

Também lida corretamente com o estouro, passando neste teste:

@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});
}
Brandon
fonte
impressionante e simples. Isso é de algum utilitário de código-fonte aberto? (a coisa do Estreams) ou algum pedaço do seu código?
vach
Obrigado! Infelizmente, não pretendia vazar o Estreamsnome (vou removê-lo da postagem). É uma das classes de utilidade interna da nossa empresa, que usamos para suplementar java.util.stream.Streamos staticmétodos.
Brandon
3
Certo ... “incrível e simples”… Existe algum caso que esta solução lide com o qual a solução ainda mais simples de Stuart Marks ainda não lida há mais de um ano e meio atrás?
Holger
Acabei de testar sua solução com meu método de teste acima; passa. Eu estava desnecessariamente evitando transbordar, em vez de abraçá-lo como ele fez. Eu concordo que o dele é melhor. Vou editar o meu para refletir isso.
Brandon
1
@vach, você pode usar StreamExespecificando a etapa:IntStreamEx.rangeClosed(from-1, to, -1)
Tagir Valeev
34

Pergunta Geral:

O fluxo não armazena nenhum elemento.

Portanto, iterar elementos na ordem inversa não é possível sem armazenar os elementos em alguma coleção intermediária.

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

Atualização: Alterada LinkedList para ArrayDeque (melhor), veja aqui para detalhes

Impressões:

3

20

2

1

A propósito, o uso do sortmétodo não está correto como ele classifica, NÃO reverte (assumindo que o fluxo possa ter elementos não ordenados)

Pergunta específica:

Achei isso simples, mais fácil e intuitivo ( comentário copiado do @Holger )

IntStream.iterate(to - 1, i -> i - 1).limit(to - from)
Venkata Raju
fonte
3
Algumas operações de fluxo, como sortede distinctna verdade armazenam um resultado intermediário. Consulte os documentos da API do pacote para obter mais informações sobre isso.
Lii
@ Lii vejo No storagena mesma página. Mesmo ele armazena nós não pode ter acesso a esse armazenamento (assim No storageé bom eu acho)
Venkata Raju
Boa resposta. Mas como o espaço extra é usado, muitas vezes não é uma boa idéia para os programadores usar sua abordagem em coleções muito grandes.
Manu Manjunath
Gosto da simplicidade da solução do ponto de vista da compreensibilidade e do aproveitamento de um método existente em uma estrutura de dados existente ... a solução anterior com uma implementação de mapa é mais difícil de entender, mas com certeza Manu está certo, para grandes coleções, eu não usaria isso intuitivo e optaria pelo mapa acima.
Beezer
Esta é uma das poucas respostas corretas aqui. A maioria dos outros, na verdade, não inverte o fluxo, eles tentam evitar fazê-lo de alguma forma (o que só funciona sob conjuntos peculiares de circunstâncias nas quais você normalmente não precisaria reverter em primeiro lugar). Se você tentar reverter um fluxo que não se encaixa na memória, estará fazendo errado de qualquer maneira. Coloque-o em um banco de dados e obtenha um fluxo reverso usando SQL regular.
Cubic
19

sem lib externo ...

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

}
comonad
fonte
15

Se implementada Comparable<T>(ex. Integer, String, Date), Você pode fazê-lo usando Comparator.reverseOrder().

List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.stream()
     .sorted(Comparator.reverseOrder())
     .forEach(System.out::println);
YujiSoftware
fonte
6
Isso não reverte o fluxo. Classifica o fluxo na ordem inversa. Então se você tivesse Stream.of(1,3,2)o resultado seria Stream.of(3,2,1)NÃOStream.of(2,3,1)
wilmol
12

Você pode definir seu próprio coletor que coleta os elementos na ordem inversa:

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 use-o como:

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

Eu uso um ArrayList para encaminhar para inserir com eficiência os itens coletados (no final da lista) e Guava Lists.reverse para fornecer com eficiência uma visão invertida da lista sem fazer outra cópia.

Aqui estão alguns casos de teste para o coletor personalizado:

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);
    }
}
lexicalscope
fonte
9

O Cyclops-react StreamUtils possui um método de fluxo reverso ( javadoc ).

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

Ele funciona coletando para um ArrayList e, em seguida, fazendo uso da classe ListIterator que pode iterar em qualquer direção, para iterar para trás na lista.

Se você já possui uma Lista, será mais eficiente

  StreamUtils.reversedStream(Arrays.asList("1", "2", "20", "3"))
             .forEach(System.out::println);
John McClean
fonte
1
Nice, não precisava saber sobre este projeto
Vach
1
O cyclops agora também vem com Spliterators para uma reversão eficiente do fluxo (atualmente para intervalos, matrizes e listas). A criação da extensão SequenceM Stream do cyclops com SequenceM.of, SequenceM.range ou SequenceM.fromList aproveitará automaticamente os separadores eficientemente reversíveis.
John McClean
6

Eu sugeriria o uso do jOOλ , é uma ótima biblioteca que adiciona muitas funcionalidades úteis aos fluxos e lambdas do Java 8.

Você pode fazer o seguinte:

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

Simples assim. É uma biblioteca bastante leve e vale a pena adicionar a qualquer projeto Java 8.

lukens
fonte
5

Aqui está a solução que eu criei:

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

depois, usando esses comparadores:

IntStream.range(-range, 0).boxed().sorted(BY_DESCENDING_ORDER).forEach(// etc...
vach
fonte
2
Esta é apenas uma resposta para sua "pergunta específica", mas não para sua "pergunta geral".
chiccodoro
9
Collections.reverseOrder()existe desde Java 1.2 e funciona com Integer...
Holger
4

Que tal esse método utilitário?

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

Parece funcionar com todos os casos sem duplicação.

Jerome
fonte
Eu gosto muito desta solução. A maioria das respostas se divide em duas categorias: (1) Inverta a coleção e .stream (), (2) Recorra a coletores personalizados. Ambos são absolutamente desnecessários. Caso contrário, teria testemunhado algum problema sério de expressividade de linguagem no próprio JDK 8. E sua resposta prova o contrário :)
vitrums 06/07
4
List newStream = list.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList());
        newStream.forEach(System.out::println);
siddmuk2005
fonte
3

Maneira mais simples (coleta simples - suporta fluxos paralelos):

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

Maneira avançada (suporta fluxos paralelos de maneira contínua):

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

Observe que você pode estender rapidamente para outro tipo de fluxos (IntStream, ...).

Teste:

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

Resultados:

Six
Five
Four
Three
Two
One

Notas adicionais: Não simplest wayé tão útil quando usado com outras operações de fluxo (a junção de coleta quebra o paralelismo). Ele advance waynão tem esse problema e mantém também as características iniciais do fluxo, por exemplo SORTED, e, portanto, é o caminho a ser usado com outras operações de fluxo após o inverso.

Tet
fonte
2

Pode-se escrever um coletor que coleta elementos em ordem inversa:

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

E use-o assim:

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

Resposta original (contém um erro - ele não funciona corretamente para fluxos paralelos):

Um método reverso de fluxo de uso geral pode se parecer com:

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

Não é puramente Java8, mas se você usar o método Lists.reverse () da guava em conjunto, poderá conseguir isso facilmente:

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

No que diz respeito à questão específica de gerar um reverso IntStream:

a partir do Java 9, você pode usar a versão de três argumentos do 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

Onde:

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

  • seed - o elemento inicial;
  • hasNext - um predicado a ser aplicado aos elementos para determinar quando o fluxo deve terminar;
  • next - uma função a ser aplicada ao elemento anterior para produzir um novo elemento.
Oleksandr Pyrohov
fonte
1

Para referência, eu estava olhando para o mesmo problema, queria juntar o valor da string dos elementos do fluxo na ordem inversa.

itemList = {último, meio, primeiro} => primeiro, meio, último

Comecei a usar uma coleção intermediária com collectingAndThende comonad ou o ArrayDequecolecionador de Stuart Marks , embora não estivesse feliz com a coleção intermediária e transmitindo novamente

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

Então, eu repeti a resposta de Stuart Marks que estava usando a Collector.offábrica, que tem o interessante finalizador lambda.

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

Como nesse caso o fluxo não é paralelo, o combinador não é relevante, estou usando insertassim mesmo para manter a consistência do código, mas isso não importa, pois dependeria de qual construtor de string é construído primeiro.

Eu olhei para o StringJoiner, no entanto, ele não tem um insertmétodo.

Brice
fonte
1

Respondendo a perguntas específicas de reversão com o IntStream, abaixo funcionou para mim:

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

ArrayDequesão mais rápidos na pilha do que uma Stack ou LinkedList. "push ()" insere elementos na frente do Deque

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

Inverter sequência ou qualquer matriz

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

A divisão pode ser modificada com base no delimitador ou no espaço

sai manoj
fonte
1

a solução mais simples é usar 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);
Adrian
fonte
Vale a pena adicionar que Stream.generate()gera em fluxo infinito, então a chamada para limit()é muito importante aqui.
55580 Andrebrait
0

É assim que eu faço.

Não gosto da ideia de criar uma nova coleção e inverter a iteração.

A ideia do mapa IntStream # é bastante interessante, mas eu prefiro o método iterado IntStream #, pois acho que a idéia de uma contagem regressiva para Zero melhor expressa com o método iterado e mais fácil de entender em termos de caminhar a matriz de trás para a frente.

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

Aqui estão alguns testes para provar que funciona:

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

Em tudo isso, não vejo a resposta que gostaria de ir primeiro.

Esta não é exatamente uma resposta direta à pergunta, mas é uma solução potencial para o problema.

Basta criar a lista ao contrário em primeiro lugar. Se possível, use um LinkedList em vez de um ArrayList e, quando adicionar itens, use "Push" em vez de adicionar. A lista será criada na ordem inversa e será transmitida corretamente, sem qualquer manipulação.

Isso não se aplica aos casos em que você está lidando com matrizes ou listas primitivas que já são usadas de várias maneiras, mas funciona bem em um número surpreendente de casos.

Bill K
fonte
0

Este método funciona com qualquer fluxo e é compatível com 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);
David Larochette
fonte
-1

A maneira mais genérica e mais fácil de reverter uma lista será:

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

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

    }
parmeshwor11
fonte
4
Você viola o contrato de Comparator. Como resultado, ninguém pode garantir que esse "truque" funcione em qualquer versão futura do Java com qualquer algoritmo de classificação. O mesmo truque não funciona para fluxo paralelo, por exemplo, como o algoritmo de classificação paralela usa de Comparatormaneira diferente. Para classificação seqüencial, funciona puramente por acaso. Eu não recomendaria ninguém para usar esta solução.
Tagir Valeev 21/10
1
Além disso, ele não funciona quando você defineSystem.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Tagir Valeev
Eu pensei que era apenas sobre a impressão coisas em ordem inversa não em ordem de classificação, e funciona com fluxo paralelo também (ie ordem decrescente.): public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
parmeshwor11
1
Tente reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))(o resultado pode depender do número de núcleos).
Tagir Valeev
-1

Java 8 maneira de fazer isso:

    List<Integer> list = Arrays.asList(1,2,3,4);
    Comparator<Integer> comparator = Integer::compare;
    list.stream().sorted(comparator.reversed()).forEach(System.out::println);
Antonio
fonte
4
Isso está ordenando na ordem inversa, não revertendo uma lista.
Jochen Bedersdorfer