Зворотний потік потоку 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
поступати

Гаразд, я знайшов це, вам потрібно використовувати .boxed (), а потім .sorted ()
vach

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

1
Ваше загальне запитання та ваше конкретне запитання читали як два повних різні питання до мене. Загальний говорить про реверсування потоку , тоді як конкретний говорить про впорядкування номерів у порядку зменшення. Якщо потік видає номери не упорядкованим чином, як 1, 3, 2очікується результат? Ви хочете, щоб перевернутий потік, як 2, 3, 1або відсортований потік 3, 2, 1?
чікодоро

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засноване рішення. Під час написання коментаря я не знав про різницю ефективності.
Хольгер

48

Елегантне рішення

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

26
Це припускаючи, що ми хочемо, щоб елементи були відсортовані у зворотному порядку. Питання полягає в тому, щоб змінити порядок потоку.
Діллон Райан Реддінг

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) чи якийсь фрагмент вашого коду?
vach

Дякую! На жаль, я не мав наміру просочувати Estreamsназву (я збираюся її видалити з посади). Це одна з внутрішніх службових класів нашої компанії, які ми використовуємо для доповнення java.util.stream.Stream«s staticметод.
Брендон

3
Гаразд ... "приголомшливий і простий" ... Чи є такий випадок, що це рішення справляється з тим, що ще простіше рішення Стюарта Маркса вже не справлялося більше півтора року тому?
Холгер

Я просто випробував його рішення моїм методом тестування вище; це проходить. Я надмірно уникав переповнення, а не сприймав його так, як він це робив. Я згоден, що його краще. Я відредагую свою, щоб це відобразити.
Брендон

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

34

Загальне питання:

Потік не зберігає жодних елементів.

Таким чином, повторення елементів у зворотному порядку неможливо без збереження елементів у деякій проміжної колекції.

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.
Кубік

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

}

15

У разі реалізації 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);

6
Це не повертає потік. Він сортує потік у зворотному порядку. Тож якби у вас 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
Ніцца не знав про цей проект
vach

1
Циклопи тепер також поставляються зі сплітераторами для ефективного обертання потоку (зараз для діапазонів, масивів та списків). Створення циклопів розширення Stream 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
Це лише відповідь на ваше "конкретне запитання", але не на ваше "загальне запитання".
чікодоро

9
Collections.reverseOrder()існує з Java 1.2 і працює з Integer
Holger

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, але якщо ви використовуєте метод Lista.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()цього дуже важливий.
andrebrait

0

Ось як я це роблю.

Мені не подобається ідея створення нової колекції та зворотного її повторення.

Ідея карти IntStream # досить акуратна, але я віддаю перевагу методу IntStream # iterate, тому що я думаю, що ідея відліку до нуля краще виражена методом ітерації та легше зрозуміти в плані проходження масиву зі спини вперед.

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.