Java 8 поток в обратном порядке


153

Общий вопрос: как правильно реверсировать поток? Предполагая, что мы не знаем, из какого типа элементов состоит этот поток, каков общий способ реверсирования любого потока?

Конкретный вопрос:

IntStreamпредоставляет метод диапазона для генерации целых чисел в определенном диапазоне IntStream.range(-range, 0), теперь, когда я хочу изменить его, диапазон переключения с 0 на отрицательный не будет работать, также я не могу использоватьInteger::compare

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

с IntStreamя получу эту ошибку компилятора

Ошибка: (191, 0) ajc: метод sorted()в типе IntStreamне применим для аргументов ( Integer::compare)

что мне здесь не хватает?


1
У Ан IntStreamнет .sorted(Comparator)метода; Вы должны пройти Stream<Integer>сначала и повернуть туда, чтобы получитьIntStream
fge

Хорошо, я нашел это, вам нужно использовать .boxed (), а затем .sorted ()
vach

4
Чтобы создать IntStream.range(0, n)в обратном порядке, сделайте что-то вроде map(i -> n - i - 1). Не нужно заниматься боксом и сортировкой.
Стюарт Маркс

1
Ваш общий вопрос и ваш конкретный вопрос для меня читаются как два совершенно разных вопроса. Общее говорит о реверсе потока , в то время как конкретное говорит о порядке номеров в порядке убывания. Если поток генерирует числа в неупорядоченном порядке 1, 3, 2, каков, каков ваш ожидаемый результат? Вы хотите обратный поток, как 2, 3, 1или отсортированный поток, как 3, 2, 1?
chiccodoro

8
Вы не можете полностью изменить поток - например, поток может быть бесконечным.
assylias

Ответы:


78

Для конкретного вопроса генерации реверса IntStreamпопробуйте что-то вроде этого:

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

Это позволяет избежать упаковки и сортировки.

Что касается общего вопроса о том, как перевернуть поток любого типа, я не знаю, есть ли «правильный» способ. Есть несколько способов, о которых я могу думать. Оба заканчивают тем, что сохранили элементы потока. Я не знаю способа перевернуть поток без сохранения элементов.

Этот первый способ сохраняет элементы в массив и считывает их в поток в обратном порядке. Обратите внимание, что, поскольку мы не знаем тип времени выполнения элементов потока, мы не можем правильно набрать массив, что требует неконтролируемого приведения.

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

Другой метод использует коллекционеров для накопления предметов в обратном списке. Это делает много вставок перед ArrayListобъектами, поэтому происходит много копий.

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

Вероятно, можно написать гораздо более эффективный реверсивный коллектор, используя какую-то настроенную структуру данных.

ОБНОВЛЕНИЕ 2016-01-29

Поскольку этот вопрос в последнее время привлек немного внимания, я решил обновить свой ответ, чтобы решить проблему со вставкой в ​​начале ArrayList. Это будет ужасно неэффективно с большим количеством элементов, требующих копирования O (N ^ 2).

ArrayDequeВместо этого предпочтительнее использовать метод, который эффективно поддерживает вставку спереди. Небольшая морщина в том, что мы не можем использовать форму с тремя аргументами Stream.collect(); для этого требуется, чтобы содержимое второго аргумента было объединено с первым аргументом, а массовая операция «добавить все впереди» не включена Deque. Вместо этого мы используем addAll()добавление содержимого первого аргумента к концу второго, а затем возвращаем второе. Это требует использования Collector.of()заводского метода.

Полный код такой:

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

Результатом является « Dequeвместо» List, но это не должно быть большой проблемой, так как его можно легко повторять или передавать в обратном порядке.


15
В качестве альтернативы:IntStream.iterate(to-1, i->i-1).limit(to-from)
Хольгер

2
@Holger К сожалению, это решение неправильно обрабатывает переполнение.
Брэндон

2
@Brandon Mintern: на самом деле, вам придется использовать .limit(endExcl-(long)startIncl)вместо этого, но для таких больших потоков, это в любом случае очень не рекомендуется, так как это гораздо менее эффективно, чем rangeрешение на основе. На момент написания комментария я не знал о разнице в эффективности.
Хольгер

49

Элегантное решение

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
Это элегантно, но не полностью работает, так как кажется, что элементы списка должны быть Comparable...
Krzysztof Wolny

27
Это предполагает, что мы хотим, чтобы элементы сортировались в обратном порядке. Вопрос в том, чтобы изменить порядок потока.
Диллон Райан Реддинг

37

Многие из решений здесь сортируются или изменяются IntStream, но это излишне требует промежуточного хранения. Решение Стюарта Маркса - это путь:

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

Он также правильно обрабатывает переполнение, пройдя этот тест:

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

офигенно и просто. Это из какой-то утилиты с открытым исходным кодом? (вещь Estreams) или какой-то кусок вашего кода?
ВАЧ

Спасибо! К сожалению, я не собирался раскрывать Estreamsимя (я собираюсь удалить его из поста). Это одна из внутренних служебных классов нашей компании, которые мы используем для дополнения java.util.stream.Stream«s staticметод.
Брэндон

3
Ладно ... «круто и просто»… Есть ли какой-нибудь случай, с которым справится это решение, которое еще более простое решение Стюарта Маркса не обрабатывало более полутора лет назад?
Хольгер

Я только что проверил его решение с моим методом испытаний выше; это проходит. Я излишне избегал переполнения вместо того, чтобы принять его, как он. Я согласен, что его лучше. Я отредактирую мой, чтобы отразить это.
Брэндон

1
@vach, вы можете использовать, StreamExуказав шаг:IntStreamEx.rangeClosed(from-1, to, -1)
Тагир Валеев

34

Общий вопрос:

Stream не хранит никаких элементов.

Поэтому итерация элементов в обратном порядке невозможна без сохранения элементов в некоторой промежуточной коллекции.

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

Обновление: изменен LinkedList на ArrayDeque (лучше), смотрите здесь для деталей

Печать:

3

20

2

1

Между прочим, использование sortметода не является правильным, так как он сортирует, а НЕ переворачивает (при условии, что поток может иметь неупорядоченные элементы)

Конкретный вопрос:

Я нашел это простым, легким и интуитивно понятным (Скопированный комментарий @Holger )

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

3
Некоторая потоковая операция, например, sortedи distinctфактически сохраняет промежуточный результат. Смотрите документацию по пакету API для получения дополнительной информации об этом.
Лий

@Lii я вижу No storageна той же странице. Даже в хранилищах мы не можем получить доступ к этому хранилищу ( No storageя думаю, это нормально)
Венката Раджу

Хороший ответ. Но поскольку используется дополнительное пространство, программистам не стоит использовать ваш подход к очень большой коллекции.
Ману Манджунат

Мне нравится простота решения с точки зрения понятности и использования существующего метода в существующей структуре данных ... решение, предшествующее реализации карты, сложнее для понимания, но наверняка Ману прав, для больших коллекций я бы не использовал это интуитивно понятный, и выбрал бы карту выше.
Beezer

Это один из немногих правильных ответов здесь. Большинство других на самом деле не переворачивают поток, они пытаются как-то избежать этого (что работает только при особых обстоятельствах, при которых вам обычно не нужно было бы возвращаться в первую очередь). Если вы пытаетесь обратить поток, который не помещается в память, вы все равно делаете это неправильно. Сбросьте его в БД и получите обратный поток, используя обычный SQL.
Cubic

19

без внешней библиотеки ...

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

В случае реализации Comparable<T>(напр. Integer, String, Date), Вы можете сделать это с помощью Comparator.reverseOrder().

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

7
Это не меняет поток. Сортирует поток в обратном порядке. Так что, если бы у вас был Stream.of(1,3,2)результат, Stream.of(3,2,1)НЕТStream.of(2,3,1)
wilmol

12

Вы можете определить свой собственный сборщик, который собирает элементы в обратном порядке:

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

И используйте это как:

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

Я использую ArrayList в прямом порядке, чтобы эффективно вставлять коллекцию элементов (в конце списка), и Guava Lists.reverse, чтобы эффективно отображать список в обратном порядке, не делая еще одну его копию.

Вот несколько тестов для пользовательского сборщика:

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

Циклоп -реакция StreamUtils имеет метод обратного потока ( Javadoc ).

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

Он работает, собирая в ArrayList, а затем используя класс ListIterator, который может выполнять итерацию в любом направлении, для итерации по списку.

Если у вас уже есть список, он будет более эффективным

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

1
Ницца не знала об этом проекте
вач

1
Циклоп теперь также поставляется с Spliterators для эффективного реверсирования потока (в настоящее время для диапазонов, массивов и списков). Создание циклопов Расширение потока SequenceM с помощью SequenceM.of, SequenceM.range или SequenceM.fromList автоматически использует преимущества эффективно обратимых сплитераторов.
Джон МакКлин

6

Я бы предложил использовать jOOλ , это отличная библиотека, которая добавляет множество полезных функций в потоки и лямбды Java 8.

Затем вы можете сделать следующее:

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

Просто как тот. Это довольно легкая библиотека, которую стоит добавить в любой проект Java 8.


5

Вот решение, которое я придумала:

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

затем с помощью этих компараторов:

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

2
Это только ответ на ваш «конкретный вопрос», но не ваш «общий вопрос».
chiccodoro

9
Collections.reverseOrder()существует с Java 1.2 и работает с Integer
Хольгер

4

Как насчет этого вспомогательного метода?

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

Кажется, работает со всеми делами без дублирования.


Мне очень нравится это решение. Большинство ответов делятся на две категории: (1) обратный сбор и .stream () его, (2) обращение к пользовательским сборщикам. Оба абсолютно не нужны. В противном случае это свидетельствовало бы о некоторой серьезной проблеме выразительности языка в самой JDK 8. И ваш ответ доказывает обратное :)
vitrums

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

3

Самый простой способ (простой сбор - поддерживает параллельные потоки):

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

Продвинутый способ (поддерживает параллельные потоки на постоянной основе):

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

Обратите внимание, что вы можете быстро распространяться на другие типы потоков (IntStream, ...).

Тестирование:

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

Полученные результаты:

Six
Five
Four
Three
Two
One

Дополнительные примечания:simplest way это не так полезно при использовании с другими операциями потока (сборным присоединиться разбивает этот параллелизм). Эта advance wayпроблема не возникает, и, к примеру SORTED, она также сохраняет начальные характеристики потока, и поэтому после обратного это способ использовать с другими операциями потока.


2

Можно написать коллектор, который собирает элементы в обратном порядке:

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

И используйте это так:

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

Оригинальный ответ (содержит ошибку - он не работает правильно для параллельных потоков):

Метод обратного потока общего назначения может выглядеть следующим образом:

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

2

Не только Java8, но если вы используете метод Lists.reverse () в guava вместе, вы можете легко добиться этого:

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

2

Что касается конкретного вопроса генерации реверса IntStream:

начиная с Java 9 вы можете использовать версию с тремя аргументами 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

где:

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

  • seed - начальный элемент;
  • hasNext - предикат, применяемый к элементам, чтобы определить, когда поток должен завершиться;
  • next - функция, которая будет применена к предыдущему элементу для создания нового элемента.

1

Для справки я смотрел на ту же проблему, я хотел соединить строковое значение элементов потока в обратном порядке.

itemList = {последний, средний, первый} => первый, средний, последний

Я начал использовать промежуточную коллекцию collectingAndThenиз комонады или ArrayDequeсобирателя Стюарта знаков , хотя я не был счастлив с промежуточной коллекцией, и снова потоковым

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

Поэтому я перебрал ответ Стюарта Маркса, который использовал Collector.ofфабрику, которая имеет интересную лямбду- финишер .

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

Так как в этом случае поток не параллелен, объединитель не так уж важен, я все insertравно использую его для согласованности кода, но это не имеет значения, так как это будет зависеть от того, какой из сборщиков строк будет построен первым.

Я посмотрел на StringJoiner, однако у него нет insertметода.


1

Отвечая на конкретный вопрос об обращении с IntStream, у меня сработало ниже:

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

1

ArrayDequeбыстрее в стеке, чем Stack или LinkedList. «push ()» вставляет элементы в передней части Deque

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

1

Обратная строка или любой массив

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

разделение может быть изменено в зависимости от разделителя или пробела


1

самое простое решение использует List::listIteratorиStream::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);

Стоит добавить, что Stream.generate()генерируется в бесконечном потоке, поэтому limit()здесь очень важен вызов to .
Андрэбрей

0

Вот как я это делаю.

Мне не нравится идея создания новой коллекции и ее повторения.

Идея карты IntStream # довольно изящна, но я предпочитаю метод итерации IntStream #, так как я думаю, что идея обратного отсчета до нуля лучше выражена с помощью метода итерации и ее легче понять с точки зрения перемещения массива назад-вперед.

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

Вот несколько тестов, чтобы доказать, что это работает:

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

Во всем этом я не вижу ответа, на который я бы пошел первым.

Это не совсем прямой ответ на вопрос, но это потенциальное решение проблемы.

Просто создайте список задом наперед. Если вы можете, используйте LinkedList вместо ArrayList, а при добавлении элементов используйте «Push» вместо add. Список будет построен в обратном порядке и будет корректно передаваться без каких-либо манипуляций.

Это не подходит для случаев, когда вы имеете дело с примитивными массивами или списками, которые уже используются различными способами, но хорошо работают в удивительном количестве случаев.


0

Этот метод работает с любым потоком и совместим с 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

Самый общий и самый простой способ отменить список будет:

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

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

    }

4
Вы нарушаете договор Comparator. В результате никто не может гарантировать вам, что этот «трюк» будет работать в любой будущей версии Java с любым алгоритмом сортировки. Та же самая хитрость не работает, например, для параллельного потока, поскольку алгоритм параллельной сортировки использует Comparatorпо-другому. Для последовательной сортировки это работает чисто случайно. Я бы никому не рекомендовал использовать это решение.
Тагир Валеев

1
Кроме того, это не работает, когда вы установитеSystem.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Тагир Валеев

Я думал, что это просто печать вещей в обратном порядке, а не в отсортированном порядке (т.е. в порядке убывания), и он работает также с параллельным потоком: public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
parmeshwor11

1
Попробуйте reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))(хотя результат может зависеть от количества ядер).
Тагир Валеев

-1

Java 8 способ сделать это:

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

4
Это сортировка в обратном порядке, а не восстановление списка.
Йохен Бедерсдорфер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.