Java 8 secuencia de orden inverso

153

Pregunta general: ¿Cuál es la forma correcta de revertir una secuencia? Suponiendo que no sabemos en qué tipo de elementos consta ese flujo, ¿cuál es la forma genérica de revertir cualquier flujo?

Pregunta especifica:

IntStreamproporciona un método de rango para generar números enteros en un rango específico IntStream.range(-range, 0), ahora que quiero revertirlo, cambiar el rango de 0 a negativo no funcionará, tampoco puedo usarInteger::compare

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

con IntStreamvoy a obtener este error del compilador

Error: (191, 0) ajc: el método sorted()en el tipo IntStreamno es aplicable para los argumentos ( Integer::compare)

¿que me estoy perdiendo aqui?

vach
fuente
1
An IntStreamno tiene .sorted(Comparator)método; tienes que pasar por un Stream<Integer>primero y revertir allí antes de ceder unIntStream
fge
Ok, lo he encontrado, necesitas usar .boxed () y luego .sorted ()
vach
44
Para generar un IntStream.range(0, n)orden inverso, haga algo como map(i -> n - i - 1). No es necesario hacer boxeo y clasificación.
Stuart Marks
1
Su pregunta general y su pregunta específica se leen como dos preguntas completas diferentes para mí. El general habla de invertir la corriente , mientras que el específico habla de ordenar los números en orden descendente. Si la secuencia produce los números de manera desordenada 1, 3, 2, ¿cuál es el resultado esperado? ¿Desea la transmisión inversa como 2, 3, 1o la transmisión ordenada como 3, 2, 1?
chiccodoro
8
No puede revertir una secuencia en general; por ejemplo, una secuencia puede ser infinita.
assylias

Respuestas:

78

Para la pregunta específica de generar un reverso IntStream, intente algo como esto:

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

Esto evita el boxeo y la clasificación.

Para la pregunta general de cómo revertir un flujo de cualquier tipo, no sé si hay una forma "adecuada". Hay un par de maneras en las que puedo pensar. Ambos terminan almacenando los elementos de la secuencia. No sé cómo revertir una secuencia sin almacenar los elementos.

Esta primera forma almacena los elementos en una matriz y los lee en una secuencia en orden inverso. Tenga en cuenta que, dado que no conocemos el tipo de tiempo de ejecución de los elementos de transmisión, no podemos escribir la matriz correctamente, lo que requiere una conversión no 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]);
}

Otra técnica utiliza coleccionistas para acumular los elementos en una lista inversa. Esto hace muchas inserciones en la parte frontal de los ArrayListobjetos, por lo que hay muchas copias en curso.

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

Probablemente sea posible escribir un recopilador de inversión mucho más eficiente utilizando algún tipo de estructura de datos personalizada.

ACTUALIZACIÓN 2016-01-29

Dado que esta pregunta ha recibido un poco de atención recientemente, creo que debería actualizar mi respuesta para resolver el problema de inserción al frente ArrayList. Esto será terriblemente ineficiente con una gran cantidad de elementos, que requieren copia de O (N ^ 2).

Es preferible usar un ArrayDequelugar, que admite de manera eficiente la inserción en la parte delantera. Una pequeña arruga es que no podemos usar la forma de tres argumentos de Stream.collect(); requiere que el contenido del segundo argumento se fusione con el primer argumento, y no hay una operación masiva "agregar todo al frente" Deque. En su lugar, usamos addAll()para agregar el contenido del primer argumento al final del segundo, y luego devolvemos el segundo. Esto requiere el uso del Collector.of()método de fábrica.

El código completo es este:

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

El resultado es un en Dequelugar de un List, pero eso no debería ser un gran problema, ya que se puede iterar o transmitir fácilmente en el orden ahora invertido.

Stuart Marks
fuente
15
Alternativamente:IntStream.iterate(to-1, i->i-1).limit(to-from)
Holger
2
@Holger Desafortunadamente, esa solución no maneja el desbordamiento correctamente.
Brandon
2
@Brandon Mintern: de hecho, tendría que usarlo .limit(endExcl-(long)startIncl)en su lugar, pero para transmisiones tan grandes, de todos modos, se desaconseja mucho ya que es mucho menos eficiente que la rangesolución basada. Cuando escribí el comentario, no sabía la diferencia de eficiencia.
Holger
48

Solución 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
fuente
66
Es elegante pero no funciona completamente, ya que parece que los elementos de la lista deben ser Comparable...
Krzysztof Wolny
26
Eso supone que queremos que los elementos se ordenen en orden inverso. La pregunta es sobre invertir el orden de una secuencia.
Dillon Ryan Redding el
37

Muchas de las soluciones aquí clasifican o invierten IntStream, pero eso requiere innecesariamente un almacenamiento intermedio. La solución de Stuart Marks es el camino a seguir:

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

También maneja correctamente el desbordamiento, pasando esta prueba:

@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
fuente
Impresionante y simple. ¿Es esto de alguna utilidad de código abierto? (lo de Estreams) o alguna parte de tu código?
vach
¡Gracias! Desafortunadamente, no tenía la intención de filtrar el Estreamsnombre (voy a eliminarlo de la publicación). Es una de las clases de utilidad interna de nuestra empresa, que utilizamos para complementar java.util.stream.Streamlos staticmétodos.
Brandon
3
De acuerdo ... "increíble y simple" ... ¿Hay algún caso que maneje esta solución, que la solución aún más simple de Stuart Marks ya no manejaba hace más de un año y medio?
Holger
Acabo de probar su solución con mi método de prueba anterior; pasó. Estaba evitando innecesariamente el desbordamiento en lugar de abrazarlo como él lo hizo. Estoy de acuerdo en que el suyo es mejor. Editaré el mío para reflejar eso.
Brandon
1
@vach, puede usar StreamExespecificando el paso:IntStreamEx.rangeClosed(from-1, to, -1)
Tagir Valeev
34

Pregunta general:

Stream no almacena ningún elemento.

Por lo tanto, no es posible iterar elementos en el orden inverso sin almacenar los elementos en alguna colección intermedia.

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

Actualización: Cambió LinkedList a ArrayDeque (mejor) ver aquí para más detalles

Huellas dactilares:

3

20

2

1

Por cierto, el uso del sortmétodo no es correcto, ya que ordena, NO se invierte (suponiendo que la secuencia puede tener elementos desordenados)

Pregunta especifica:

Me encontré con este sencillo, fácil e intuitiva (@Holger copiado comentario )

IntStream.iterate(to - 1, i -> i - 1).limit(to - from)
Venkata Raju
fuente
3
Algunas operaciones de flujo, como sortedy distinctrealmente almacenan un resultado intermedio. Consulte los documentos de la API del paquete para obtener información al respecto.
Lii
@Lii veo No storageen la misma página. Incluso almacena, no podemos acceder a ese almacenamiento (así que No storageestá bien, supongo)
Venkata Raju
Buena respuesta. Pero dado que se utiliza espacio adicional, puede que no sea una gran idea que los programadores usen su enfoque en una colección muy grande.
Manu Manjunath
Me gusta la simplicidad de la solución desde una perspectiva comprensible y aprovechar un método existente en una estructura de datos existente ... la solución anterior con una implementación de mapa es más difícil de entender, pero seguro que Manu tiene razón, para colecciones grandes no usaría esto intuitivo, y optaría por el mapa de arriba.
Beezer
Esta es una de las pocas respuestas correctas aquí. La mayoría de los otros en realidad no invierten la transmisión, intentan evitar hacerlo de alguna manera (que solo funciona bajo circunstancias particulares en las que normalmente no necesitarías invertir en primer lugar). Si intentas revertir una secuencia que no cabe en la memoria, lo estás haciendo mal de todos modos. Viértalo en una base de datos y obtenga una secuencia invertida usando SQL regular.
Cubic
19

sin 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
fuente
15

Si se aplica Comparable<T>(por ej. Integer, String, Date), Puede hacerlo utilizando Comparator.reverseOrder().

List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.stream()
     .sorted(Comparator.reverseOrder())
     .forEach(System.out::println);
YujiSoftware
fuente
66
Esto no revierte la corriente. Ordena la secuencia en orden inverso. Entonces, si tuviera Stream.of(1,3,2)el resultado, Stream.of(3,2,1)NO seríaStream.of(2,3,1)
wilmol
12

Puede definir su propio recopilador que recopila los elementos en orden 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);
}

Y úsalo como:

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

Utilizo una ArrayList en orden de avance para insertar eficientemente los elementos de recopilación (al final de la lista) y Guava Lists.verse para dar una vista inversa de la lista sin hacer otra copia.

Estos son algunos casos de prueba para el recopilador 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);
    }
}
telescopio léxico
fuente
9

StreamUtils de cyclops- react tiene un método de flujo inverso ( javadoc ).

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

Funciona al recopilar en una ArrayList y luego hacer uso de la clase ListIterator que puede iterar en cualquier dirección, para iterar hacia atrás sobre la lista.

Si ya tiene una Lista, será más eficiente

  StreamUtils.reversedStream(Arrays.asList("1", "2", "20", "3"))
             .forEach(System.out::println);
John McClean
fuente
1
Nice no sabía sobre este proyecto
vach
1
cyclops ahora también viene con Spliterators para una inversión eficiente de Stream (actualmente para Rangos, arrays y Listas). La creación de la extensión Cyclops SequenceM Stream con SequenceM.of, SequenceM.range o SequenceM.fromList aprovechará automáticamente los spliteradores reversibles de manera eficiente.
John McClean
6

Sugeriría usar jOOλ , es una gran biblioteca que agrega muchas funcionalidades útiles a las secuencias y lambdas de Java 8.

Luego puede hacer lo siguiente:

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

Simple como eso. Es una biblioteca bastante ligera y vale la pena agregarla a cualquier proyecto Java 8.

lukens
fuente
5

Aquí está la solución que se me ocurrió:

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

luego usando esos comparadores:

IntStream.range(-range, 0).boxed().sorted(BY_DESCENDING_ORDER).forEach(// etc...
vach
fuente
2
Esta es solo una respuesta a su "pregunta específica" pero no a su "pregunta general".
chiccodoro
9
Collections.reverseOrder()existe desde Java 1.2 y funciona con Integer...
Holger
4

¿Qué tal este método de utilidad?

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 con todos los casos sin duplicación.

Jerome
fuente
Me gusta mucho esta solución. La mayoría de las respuestas se dividen en dos categorías: (1) Invierte la colección y .stream (), (2) Apela a los recolectores personalizados. Ambos son absolutamente innecesarios. De lo contrario, habría testificado sobre algún problema serio de expresividad del lenguaje en JDK 8. Y tu respuesta demuestra lo contrario :)
vitrums
4
List newStream = list.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList());
        newStream.forEach(System.out::println);
siddmuk2005
fuente
3

La forma más simple (recolección simple: admite flujos 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();
}

Forma avanzada (admite secuencias paralelas de forma continua):

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

Tenga en cuenta que puede extenderse rápidamente a otro tipo de secuencias (IntStream, ...).

Pruebas:

// 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 adicionales: No simplest wayes tan útil cuando se usa con otras operaciones de flujo (la combinación de recopilación rompe el paralelismo). El advance wayno tiene ese problema, y ​​también mantiene las características iniciales de la transmisión, por ejemplo SORTED, y así, es la forma de usarlo con otras operaciones de transmisión después de lo contrario.

Tet
fuente
2

Se podría escribir un recopilador que recopile elementos en orden inverso:

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

Y úsalo así:

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

Respuesta original (contiene un error, no funciona correctamente para flujos paralelos):

Un método inverso de flujo de propósito general podría verse así:

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

No solo Java8, pero si usa el método Lists.reverse () de guava en conjunto, puede lograr esto fácilmente:

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

Con respecto a la cuestión específica de generar un reverso IntStream:

a partir de Java 9 , puede usar la versión de tres argumentos de 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

dónde:

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

  • seed - el elemento inicial;
  • hasNext - un predicado para aplicar a los elementos para determinar cuándo debe terminar la secuencia;
  • next - una función que se aplicará al elemento anterior para producir un nuevo elemento.
Oleksandr Pyrohov
fuente
1

Como referencia, estaba viendo el mismo problema, quería unir el valor de cadena de los elementos de flujo en el orden inverso.

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

Comencé a usar una colección intermedia collectingAndThende comonad o el ArrayDequecoleccionista de Stuart Marks , aunque no estaba contento con la colección intermedia y la transmisión de nuevo

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

Así que repetí la respuesta de Stuart Marks que estaba usando la Collector.offábrica, que tiene el interesante acabado lambda.

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

Dado que en este caso la secuencia no es paralela, el combinador no es tan relevante, lo uso de inserttodos modos en aras de la coherencia del código, pero no importa, ya que dependería de qué constructor de cadenas se construya primero.

Miré el StringJoiner, sin embargo, no tiene un insertmétodo.

Brice
fuente
1

Respondiendo la pregunta específica de revertir con IntStream, a continuación funcionó para mí:

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

ArrayDequeson más rápidos en la pila que un Stack o LinkedList. "push ()" inserta elementos en la parte frontal del Deque

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

Cadena de inversión o cualquier matriz

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

la división se puede modificar según el delimitador o el espacio

sai manoj
fuente
1

la solución más simple es usar List::listIteratoryStream::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
fuente
Vale la pena agregar que Stream.generate()genera en flujo infinito, por lo que la llamada a limit()es muy importante aquí.
andrebrait
0

Así es como lo hago.

No me gusta la idea de crear una nueva colección y revertirla.

La idea del mapa IntStream # es bastante clara, pero prefiero el método iterativo IntStream #, porque creo que la idea de una cuenta regresiva a cero se expresa mejor con el método iterativo y es más fácil de entender en términos de caminar la matriz de atrás hacia adelante.

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

Aquí hay algunas pruebas para demostrar 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
fuente
0

En todo esto no veo la respuesta a la que iría primero.

Esta no es exactamente una respuesta directa a la pregunta, pero es una posible solución al problema.

Simplemente construya la lista al revés en primer lugar. Si puede, use una LinkedList en lugar de una ArrayList y cuando agregue elementos use "Push" en lugar de agregar. La lista se construirá en el orden inverso y luego se transmitirá correctamente sin ninguna manipulación.

Esto no se ajusta a los casos en los que se trata de matrices o listas primitivas que ya se utilizan de varias maneras, pero que funcionan bien en un sorprendente número de casos.

Bill K
fuente
0

Este método funciona con cualquier Stream y es compatible con 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
fuente
-1

La forma más genérica y más fácil de revertir una lista será:

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

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

    }
parmeshwor11
fuente
44
Usted viola el contrato de Comparator. Como resultado, nadie puede garantizarle que este "truco" funcionará en cualquier versión futura de Java con cualquier algoritmo de clasificación. El mismo truco no funciona para el flujo paralelo, por ejemplo, ya que el algoritmo de ordenamiento paralelo se usa Comparatorde manera diferente. Para la ordenación secuencial, funciona por pura casualidad. No recomendaría a nadie que use esta solución.
Tagir Valeev
1
Tampoco funciona cuando configurasSystem.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Tagir Valeev
Pensé que solo se trataba de imprimir cosas en orden inverso, no en orden ordenado (es decir, en orden descendente), y también funciona con flujo paralelo: public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
parmeshwor11
1
Intente reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))(el resultado puede depender de la cantidad de núcleos).
Tagir Valeev
-1

Java 8 forma de hacer esto:

    List<Integer> list = Arrays.asList(1,2,3,4);
    Comparator<Integer> comparator = Integer::compare;
    list.stream().sorted(comparator.reversed()).forEach(System.out::println);
Antonio
fuente
44
Esto se ordena en orden inverso, no revierte una lista.
Jochen Bedersdorfer