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?


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é.


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

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

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)

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

}

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

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

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

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.


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...

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

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.


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

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

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.

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.


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

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

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


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

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

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.


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

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

    }

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

4
Il s'agit de trier dans l'ordre inverse, pas de revenir sur une liste.
Jochen Bedersdorfer
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.