Ordre inverse du flux Java 8

153

Question générale: Quelle est la bonne façon d'inverser un flux? En supposant que nous ne sachions pas de quel type d'éléments ce flux se compose, quel est le moyen générique d'inverser un flux?

Question spécifique:

IntStreamfournit une méthode de plage pour générer des entiers dans une plage spécifique IntStream.range(-range, 0), maintenant que je veux l'inverser, la commutation de la plage de 0 à négative ne fonctionnera pas, je ne peux pas non plus utiliserInteger::compare

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

avec IntStreamj'obtiendrai cette erreur du compilateur

Erreur: (191, 0) ajc: La méthode sorted()du type IntStreamn'est pas applicable pour les arguments ( Integer::compare)

Qu'est-ce que j'oublie ici?

vach
la source
1
An IntStreamn'a pas de .sorted(Comparator)méthode; vous devez passer par un Stream<Integer>premier et inverser là-bas avant de céder unIntStream
fge
Ok, je l'ai trouvé, vous devez utiliser .boxed () puis .sorted ()
vach
4
Pour générer un IntStream.range(0, n)dans l'ordre inverse, faites quelque chose comme map(i -> n - i - 1). Pas besoin de faire de la boxe et du tri.
Stuart marque le
1
Votre question générale et votre question spécifique se lisent comme deux questions complètement différentes pour moi. Le général parle d'inverser le flux , tandis que le spécifique parle de classer les nombres par ordre décroissant. Si le flux produit les nombres de manière non ordonnée 1, 3, 2, quel est votre résultat attendu? Voulez-vous le flux inversé 2, 3, 1ou le flux trié comme 3, 2, 1?
chiccodoro
8
Vous ne pouvez pas inverser un flux en général - par exemple, un flux peut être infini.
assylias

Réponses:

78

Pour la question spécifique de la génération d'un reverse IntStream, essayez quelque chose comme ceci:

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

Cela évite la mise en boîte et le tri.

Pour la question générale de savoir comment inverser un flux de n'importe quel type, je ne sais pas s'il existe un moyen "approprié". Je peux penser à plusieurs façons. Les deux finissent par stocker les éléments du flux. Je ne connais pas de moyen d'inverser un flux sans stocker les éléments.

Cette première méthode stocke les éléments dans un tableau et les lit dans un flux dans l'ordre inverse. Notez que puisque nous ne connaissons pas le type d'exécution des éléments de flux, nous ne pouvons pas taper le tableau correctement, ce qui nécessite un cast non vérifié.

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

Une autre technique utilise des collectionneurs pour accumuler les éléments dans une liste inversée. Cela fait beaucoup d'insertions à l'avant des ArrayListobjets, donc il y a beaucoup de copie en cours.

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

Il est probablement possible d'écrire un collecteur inverseur beaucoup plus efficace en utilisant une sorte de structure de données personnalisée.

MISE À JOUR 29/01/2016

Étant donné que cette question a attiré un peu d'attention récemment, je pense que je devrais mettre à jour ma réponse pour résoudre le problème d'insertion au début de ArrayList. Ce sera horriblement inefficace avec un grand nombre d'éléments, nécessitant une copie O (N ^ 2).

Il est préférable d'utiliser un à la ArrayDequeplace, qui prend en charge efficacement l'insertion à l'avant. Une petite ride est que nous ne pouvons pas utiliser la forme à trois arguments de Stream.collect(); cela nécessite que le contenu du second argument soit fusionné dans le premier argument, et il n'y a pas d'opération en bloc "ajouter tout à l'avant" Deque. Au lieu de cela, nous utilisons addAll()pour ajouter le contenu du premier argument à la fin du second, puis nous renvoyons le second. Cela nécessite l'utilisation de la Collector.of()méthode d'usine.

Le code complet est le suivant:

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

Le résultat est un Dequeau lieu d'un List, mais cela ne devrait pas être un gros problème, car il peut facilement être itéré ou diffusé dans l'ordre maintenant inversé.

Marques Stuart
la source
15
Alternativement:IntStream.iterate(to-1, i->i-1).limit(to-from)
Holger
2
@Holger Malheureusement, cette solution ne gère pas correctement le débordement.
Brandon
2
@Brandon Mintern: en effet, il faudrait utiliser à la .limit(endExcl-(long)startIncl)place, mais pour des flux aussi importants, c'est quand même très déconseillé car c'est beaucoup moins efficace que la rangesolution basée. Au moment où j'ai écrit le commentaire, je n'étais pas au courant de la différence d'efficacité.
Holger
49

Solution élégante

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
la source
6
Il est élégant mais ne fonctionne pas pleinement, car il semble que les éléments de la liste doivent être Comparable...
Krzysztof Wolny
27
Cela suppose que nous voulons que les éléments soient triés dans l'ordre inverse. La question est d'inverser l'ordre d'un flux.
Dillon Ryan Redding
37

Beaucoup de solutions ici trient ou inversent le IntStream, mais cela nécessite inutilement un stockage intermédiaire. La solution de Stuart Marks est la voie à suivre:

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

Il gère également correctement le débordement, en passant ce 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});
}
Brandon
la source
génial et simple. Est-ce un utilitaire open source? (le truc Estreams) ou un morceau de votre code?
vach
Merci! Malheureusement, je n'avais pas l'intention de divulguer le Estreamsnom (je vais le supprimer du message). C'est l'une des classes d'utilité internes de notre société, que nous utilisons pour compléter java.util.stream.Streamles staticméthodes de.
Brandon
3
D'accord… «génial et simple»… Y a-t-il un cas que cette solution gère, que la solution encore plus simple de Stuart Marks ne traitait pas déjà il y a plus d'un an et demi?
Holger
Je viens de tester sa solution avec ma méthode de test ci-dessus; ça passe. J'évitais inutilement le débordement au lieu de l'embrasser comme lui. Je suis d'accord que le sien est meilleur. Je vais modifier le mien pour refléter cela.
Brandon
1
@vach, vous pouvez utiliser en StreamExspécifiant l'étape:IntStreamEx.rangeClosed(from-1, to, -1)
Tagir Valeev
34

Question générale:

Stream ne stocke aucun élément.

Il n'est donc pas possible d'itérer les éléments dans l'ordre inverse sans stocker les éléments dans une collection intermédiaire.

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

Mise à jour: LinkedList changé en ArrayDeque (mieux) voir ici pour plus de détails

Impressions:

3

20

2

1

À propos, l'utilisation de la sortméthode n'est pas correcte car elle trie, PAS inverse (en supposant que le flux peut avoir des éléments non ordonnés)

Question spécifique:

J'ai trouvé cela simple, plus facile et intuitif ( commentaire copié sur @Holger )

IntStream.iterate(to - 1, i -> i - 1).limit(to - from)
Venkata Raju
la source
3
Certaines opérations de flux, telles que sortedet distinctstockent en fait un résultat intermédiaire. Consultez la documentation de l'API du package pour obtenir des informations à ce sujet.
Lii
@Lii je vois No storagedans la même page. Même s'il stocke, nous ne pouvons pas avoir accès à ce stockage (donc No storagec'est bien je suppose)
Venkata Raju
Bonne réponse. Mais comme un espace supplémentaire est utilisé, ce n'est pas une bonne idée pour les programmeurs d'utiliser votre approche sur une très grande collection.
Manu Manjunath
J'aime la simplicité de la solution du point de vue de la compréhensibilité et l'exploitation d'une méthode existante sur une structure de données existante ... la solution précédente avec une implémentation de carte est plus difficile à comprendre mais bien sûr, Manu a raison, pour les grandes collections, je n'utiliserais pas cela intuitif, et opterait pour la carte ci-dessus.
Beezer
C'est l'une des rares réponses correctes ici. La plupart des autres n'inversent pas réellement le flux, ils essaient d'éviter de le faire d'une manière ou d'une autre (ce qui ne fonctionne que dans des circonstances particulières dans lesquelles vous n'auriez normalement pas besoin d'inverser en premier lieu). Si vous essayez d'inverser un flux qui ne rentre pas dans la mémoire, vous le faites mal de toute façon. Videz-le dans une base de données et obtenez un flux inversé en utilisant SQL standard.
Cubic
19

sans lib externe ...

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
la source
16

Si elles sont appliquées Comparable<T>(ex. Integer, String, Date), Vous pouvez le faire en utilisant Comparator.reverseOrder().

List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.stream()
     .sorted(Comparator.reverseOrder())
     .forEach(System.out::println);
Logiciel Yuji
la source
7
Cela n'inverse pas le flux. Il trie le flux dans l'ordre inverse. Donc, si vous aviez Stream.of(1,3,2)le résultat ne serait Stream.of(3,2,1)PASStream.of(2,3,1)
wilmol
12

Vous pouvez définir votre propre collecteur qui collecte les éléments dans l'ordre inverse:

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

Et utilisez-le comme:

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

J'utilise une ArrayList pour insérer efficacement les éléments collectés (à la fin de la liste) et Guava Lists.reverse pour donner efficacement une vue inversée de la liste sans en faire une autre copie.

Voici quelques cas de test pour le collecteur personnalisé:

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
la source
9

cyclops- react StreamUtils a une méthode de flux inversé ( javadoc ).

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

Il fonctionne en collectant dans un ArrayList, puis en utilisant la classe ListIterator qui peut itérer dans les deux sens, pour parcourir la liste en arrière.

Si vous avez déjà une liste, ce sera plus efficace

  StreamUtils.reversedStream(Arrays.asList("1", "2", "20", "3"))
             .forEach(System.out::println);
John McClean
la source
1
Nice ne savait pas à propos de ce projet
vach
1
cyclops est désormais également livré avec des Spliterators pour une inversion efficace des flux (actuellement pour les plages, les tableaux et les listes). La création de l' extension de flux de cyclope SequenceM avec SequenceM.of, SequenceM.range ou SequenceM.fromList tirera automatiquement parti de séparateurs réversibles efficacement.
John McClean
6

Je suggérerais d'utiliser jOOλ , c'est une excellente bibliothèque qui ajoute de nombreuses fonctionnalités utiles aux flux et lambdas Java 8.

Vous pouvez alors effectuer les opérations suivantes:

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

Aussi simple que cela. C'est une bibliothèque assez légère, qui vaut la peine d'être ajoutée à n'importe quel projet Java 8.

lukens
la source
5

Voici la solution que j'ai trouvée:

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

puis en utilisant ces comparateurs:

IntStream.range(-range, 0).boxed().sorted(BY_DESCENDING_ORDER).forEach(// etc...
vach
la source
2
Ceci n'est qu'une réponse à votre "question spécifique" mais pas à votre "question générale".
chiccodoro
9
Collections.reverseOrder()existe depuis Java 1.2 et fonctionne avec Integer
Holger
4

Et cette méthode utilitaire?

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

Semble fonctionner avec tous les cas sans duplication.

Jérôme
la source
J'aime beaucoup cette solution. La plupart des réponses se divisent en deux catégories: (1) Inverser la collection et .stream () it, (2) Faire appel aux collectionneurs personnalisés. Les deux sont absolument inutiles. Sinon, il aurait témoigné d'un grave problème d'expressivité du langage dans le JDK 8 lui-même. Et votre réponse prouve le contraire :)
vitrums
4
List newStream = list.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList());
        newStream.forEach(System.out::println);
siddmuk2005
la source
3

Méthode la plus simple (collecte simple - prend en charge les flux parallèles):

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

Méthode avancée (prend en charge les flux parallèles de manière continue):

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

Notez que vous pouvez rapidement étendre à d'autres types de flux (IntStream, ...).

Essai:

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

Résultats:

Six
Five
Four
Three
Two
One

Notes complémentaires: Le simplest wayil n'est pas si utile lorsqu'il est utilisé avec d' autres opérations de flux (la jointure Collect rompt le parallélisme). Le advance wayn'a pas ce problème, et il conserve également les caractéristiques initiales du flux, par exemple SORTED, et donc, c'est la voie à suivre pour l'utiliser avec d'autres opérations de flux après l'inverse.

Tet
la source
2

On pourrait écrire un collecteur qui collecte les éléments dans l'ordre inverse:

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

Et utilisez-le comme ceci:

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

Réponse originale (contient un bogue - cela ne fonctionne pas correctement pour les flux parallèles):

Une méthode d'inversion de flux à usage général pourrait ressembler à:

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

Pas purement Java8 mais si vous utilisez la méthode Lists.reverse () de goyave en conjonction, vous pouvez facilement y parvenir:

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

Concernant la question spécifique de la génération d'un reverse IntStream:

à partir de Java 9, vous pouvez utiliser la version à trois arguments 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

où:

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

  • seed - l'élément initial;
  • hasNext - un prédicat à appliquer aux éléments pour déterminer quand le flux doit se terminer;
  • next - une fonction à appliquer à l'élément précédent pour produire un nouvel élément.
Oleksandr Pyrohov
la source
1

Pour référence, je regardais le même problème, je voulais joindre la valeur de chaîne des éléments de flux dans l'ordre inverse.

itemList = {dernier, milieu, premier} => premier, milieu, dernier

J'ai commencé à utiliser une collection intermédiaire avec collectingAndThende comonad ou le ArrayDequecollectionneur de Stuart Marks , même si je n'étais pas satisfait de la collection intermédiaire, et du streaming à nouveau

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

J'ai donc répété la réponse de Stuart Marks qui utilisait l' Collector.ofusine, qui a l'intéressant finisseur lambda.

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

Puisque dans ce cas, le flux n'est pas parallèle, le combineur n'est pas tellement pertinent, je l'utilise insertquand même pour des raisons de cohérence du code mais cela n'a pas d'importance car cela dépendrait du constructeur de chaînes construit en premier.

J'ai regardé le StringJoiner, mais il n'a pas de insertméthode.

Brice
la source
1

Répondre à la question spécifique de l'inversion avec IntStream, ci-dessous a fonctionné pour moi:

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

ArrayDequesont plus rapides dans la pile qu'une pile ou une LinkedList. "push ()" insère des éléments à l'avant du Deque

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

Chaîne inversée ou n'importe quel tableau

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

la division peut être modifiée en fonction du délimiteur ou de l'espace

sai manoj
la source
1

la solution la plus simple consiste à utiliser List::listIteratoretStream::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
la source
Cela vaut la peine d'ajouter que Stream.generate()génère en flux infini, donc l'appel à limit()est très important ici.
andrebrait
0

Voilà comment je fais.

Je n'aime pas l'idée de créer une nouvelle collection et de l'inverser.

L'idée de la carte IntStream # est assez soignée, mais je préfère la méthode IntStream # iterate, car je pense que l'idée d'un compte à rebours jusqu'à zéro mieux exprimée avec la méthode iterate et plus facile à comprendre en termes de marche du tableau de l'arrière vers l'avant.

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

Voici quelques tests pour prouver que cela fonctionne:

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
la source
0

Dans tout cela, je ne vois pas la réponse à laquelle j'irais en premier.

Ce n'est pas exactement une réponse directe à la question, mais c'est une solution potentielle au problème.

Construisez simplement la liste à l'envers en premier lieu. Si vous le pouvez, utilisez une LinkedList au lieu d'une ArrayList et lorsque vous ajoutez des éléments, utilisez "Push" au lieu d'ajouter. La liste sera construite dans l'ordre inverse et sera ensuite diffusée correctement sans aucune manipulation.

Cela ne convient pas aux cas où vous avez affaire à des tableaux ou des listes primitifs qui sont déjà utilisés de différentes manières, mais qui fonctionnent bien dans un nombre surprenant de cas.

Bill K
la source
0

Cette méthode fonctionne avec n'importe quel flux et est compatible 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
la source
-1

Le moyen le plus générique et le plus simple d'inverser une liste sera:

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

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

    }
parmeshwor11
la source
4
Vous violez le contrat de Comparator. En conséquence, personne ne peut vous garantir que cette "astuce" fonctionnera dans n'importe quelle future version de Java avec n'importe quel algorithme de tri. La même astuce ne fonctionne pas pour le flux parallèle, par exemple, car l'algorithme de tri parallèle l'utilise Comparatorde manière différente. Pour le tri séquentiel, cela fonctionne uniquement par hasard. Je ne recommanderais à personne d'utiliser cette solution.
Tagir Valeev
1
De plus, cela ne fonctionne pas lorsque vous définissezSystem.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Tagir Valeev
Je pensais qu'il s'agissait simplement d'imprimer les choses dans l'ordre inverse et non dans l'ordre trié (c'est-à-dire dans l'ordre décroissant), et cela fonctionne aussi avec un flux parallèle: public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
parmeshwor11
1
Essayez reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))(le résultat peut toutefois dépendre du nombre de cœurs).
Tagir Valeev
-1

Java 8 façon de faire cela:

    List<Integer> list = Arrays.asList(1,2,3,4);
    Comparator<Integer> comparator = Integer::compare;
    list.stream().sorted(comparator.reversed()).forEach(System.out::println);
Antonio
la source
4
Il s'agit de trier dans l'ordre inverse, pas de revenir sur une liste.
Jochen Bedersdorfer