Java 8 stream urutan terbalik

153

Pertanyaan umum: Apa cara yang tepat untuk membalikkan arus? Dengan asumsi bahwa kita tidak tahu jenis elemen yang terdiri dari aliran, apa cara umum untuk membalikkan aliran apa pun?

Pertanyaan spesifik:

IntStreammenyediakan metode rentang untuk menghasilkan bilangan bulat dalam rentang tertentu IntStream.range(-range, 0), sekarang saya ingin membalikkannya, beralih dari 0 ke negatif tidak akan berfungsi, juga saya tidak bisa menggunakanInteger::compare

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

dengan IntStreamsaya akan mendapatkan kesalahan kompilator ini

Kesalahan: (191, 0) ajc: Metode sorted()dalam tipe IntStreamini tidak berlaku untuk argumen ( Integer::compare)

Apa yang kulewatkan di sini?

vach
sumber
1
An IntStreamtidak memiliki .sorted(Comparator)metode; Anda harus pergi melalui Stream<Integer>pertama dan reverse sana sebelum menghasilkan sebuahIntStream
FGE
Oke, saya sudah menemukannya, Anda perlu menggunakan .boxed () dan kemudian .sorted ()
vach
4
Untuk menghasilkan IntStream.range(0, n)urutan terbalik, lakukan sesuatu seperti map(i -> n - i - 1). Tidak perlu melakukan tinju dan sortir.
Stuart Marks
1
Pertanyaan umum Anda dan pertanyaan spesifik Anda seperti dua pertanyaan lengkap yang berbeda bagi saya. Jenderal berbicara tentang membalikkan aliran , sementara spesifik berbicara tentang memesan nomor dalam urutan menurun. Jika arus menghasilkan angka-angka dengan cara yang tidak berurutan seperti 1, 3, 2, apa hasil yang Anda harapkan? Apakah Anda ingin seperti aliran terbalik 2, 3, 1atau aliran yang disortir 3, 2, 1?
chiccodoro
8
Anda tidak dapat membalikkan aliran secara umum - misalnya aliran mungkin tidak terbatas.
assylias

Jawaban:

78

Untuk pertanyaan spesifik tentang menghasilkan kebalikan IntStream, coba sesuatu seperti ini:

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

Ini menghindari tinju dan penyortiran.

Untuk pertanyaan umum tentang cara membalikkan aliran jenis apa pun, saya tidak tahu ada cara yang "tepat". Ada beberapa cara yang bisa saya pikirkan. Keduanya akhirnya menyimpan elemen stream. Saya tidak tahu cara untuk membalikkan aliran tanpa menyimpan elemen.

Cara pertama ini menyimpan elemen ke dalam array dan membacanya ke aliran dalam urutan terbalik. Perhatikan bahwa karena kita tidak tahu tipe runtime dari elemen stream, kita tidak bisa mengetikkan array dengan benar, membutuhkan cetakan yang tidak dicentang.

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

Teknik lain menggunakan kolektor untuk mengumpulkan item ke dalam daftar terbalik. Ini melakukan banyak penyisipan di bagian depanArrayList objek, jadi ada banyak penyalinan yang terjadi.

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

Mungkin untuk menulis kolektor pembalikan yang jauh lebih efisien menggunakan semacam struktur data yang disesuaikan.

UPDATE 2016-01-29

Karena pertanyaan ini telah mendapatkan sedikit perhatian baru-baru ini, saya pikir saya harus memperbarui jawaban saya untuk menyelesaikan masalah dengan memasukkan di depan ArrayList . Ini akan sangat tidak efisien dengan sejumlah besar elemen, yang membutuhkan penyalinan O (N ^ 2).

Lebih baik menggunakan yang ArrayDequesebaliknya, yang secara efisien mendukung penyisipan di bagian depan. Kerutan kecil adalah bahwa kita tidak dapat menggunakan bentuk tiga-arg Stream.collect(); itu membutuhkan isi dari arg kedua digabungkan ke dalam arg pertama, dan tidak ada operasi massal "add-all-at-front" aktif Deque. Sebagai gantinya, kita gunakan addAll()untuk menambahkan konten dari arg pertama ke akhir yang kedua, dan kemudian kita mengembalikan yang kedua. Ini mengharuskan menggunakan Collector.of()metode pabrik.

Kode lengkapnya adalah ini:

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

Hasilnya adalah Dequebukannya List, tetapi itu seharusnya tidak menjadi masalah, karena dapat dengan mudah diulangi atau dialirkan dalam urutan yang sekarang terbalik.

Stuart Marks
sumber
15
Atau:IntStream.iterate(to-1, i->i-1).limit(to-from)
Holger
2
@ Holger Sayangnya, solusi itu tidak menangani overflow dengan benar.
Brandon
2
@Brandon Mintern: memang, Anda harus menggunakan .limit(endExcl-(long)startIncl)sebagai gantinya, tetapi untuk aliran besar seperti itu, sangat tidak dianjurkan karena jauh lebih efisien daripada rangesolusi berbasis. Pada saat saya menulis komentar, saya tidak mengetahui perbedaan efisiensi.
Holger
48

Solusi elegan

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
sumber
6
Ini elegan tetapi tidak sepenuhnya berfungsi karena tampaknya unsur-unsur dalam daftar harus Comparable...
Krzysztof Wolny
26
Itu dengan asumsi kami ingin elemen diurutkan dalam urutan terbalik. Pertanyaannya adalah tentang membalik urutan aliran.
Dillon Ryan Redding
37

Banyak solusi di sini mengurutkan atau membalikkan IntStream, tetapi itu tidak perlu memerlukan penyimpanan perantara. Solusi Stuart Marks adalah caranya:

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

Itu benar menangani overflow juga, melewati tes ini:

@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
sumber
luar biasa dan sederhana. Apakah ini dari beberapa utilitas opensource? (hal Estreams) atau sebagian kode Anda?
vach
Terima kasih! Sayangnya, saya tidak bermaksud membocorkan Estreamsnama (saya akan menghapusnya dari pos). Ini salah satu kelas utilitas internal yang perusahaan kami, yang kami gunakan untuk suplemen java.util.stream.Stream's staticmetode.
Brandon
3
Oke ... "luar biasa dan sederhana" ... Apakah ada solusi yang ditangani oleh solusi ini, yang bahkan solusi lebih sederhana dari Stuart Marks belum menangani lebih dari satu setengah tahun yang lalu?
Holger
Saya baru saja menguji solusinya dengan metode pengujian saya di atas; itu berlalu. Saya tidak perlu menghindari overflow daripada merangkulnya seperti yang dia lakukan. Saya setuju bahwa itu lebih baik. Saya akan mengedit milik saya untuk mencerminkan itu.
Brandon
1
@vach, Anda dapat menggunakan dengan StreamExmenentukan langkah:IntStreamEx.rangeClosed(from-1, to, -1)
Tagir Valeev
34

Pertanyaan Umum:

Stream tidak menyimpan elemen apa pun.

Jadi elemen iterasi dalam urutan terbalik tidak mungkin tanpa menyimpan elemen dalam beberapa koleksi perantara.

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

Pembaruan: Mengubah LinkedList menjadi ArrayDeque (lebih baik) lihat di sini untuk detailnya

Cetakan:

3

20

2

1

By the way, menggunakan sort metode tidak benar karena menyortir, BUKAN terbalik (dengan asumsi aliran mungkin memiliki elemen unordered)

Pertanyaan spesifik:

Saya menemukan ini sederhana, lebih mudah dan intuitif (Salinan @ Holger komentar )

IntStream.iterate(to - 1, i -> i - 1).limit(to - from)
Venkata Raju
sumber
3
Beberapa operasi streaming, seperti sorteddan distinctbenar - benar menyimpan hasil antara. Lihat paket API docs untuk beberapa informasi tentang itu.
Lii
@ Lii saya lihat No storagedi halaman yang sama. Bahkan toko itu menyimpan kita tidak bisa mendapatkan akses ke penyimpanan itu ( No storagesaya kira baik-baik saja)
Venkata Raju
Jawaban yang bagus. Tetapi karena ruang ekstra digunakan, banyak yang tidak bisa menjadi ide bagus bagi programmer untuk menggunakan pendekatan Anda pada koleksi yang sangat besar.
Manu Manjunath
Saya suka kesederhanaan solusi dari perspektif dapat dimengerti dan memanfaatkan metode yang ada pada struktur data yang ada ... solusi sebelumnya dengan implementasi peta lebih sulit untuk dipahami tetapi pasti Manu benar, untuk koleksi besar saya tidak akan menggunakan ini intuitif, dan akan memilih peta di atas.
Beezer
Ini adalah salah satu dari beberapa jawaban yang benar di sini. Sebagian besar yang lain tidak benar-benar membalikkan arus, mereka mencoba menghindari melakukannya entah bagaimana (yang hanya berfungsi di bawah keadaan khusus di mana Anda biasanya tidak perlu membalikkan di tempat pertama). Jika Anda mencoba membalikkan aliran yang tidak sesuai dengan memori, Anda tetap salah melakukannya. Buang menjadi DB dan dapatkan aliran terbalik menggunakan SQL biasa.
Cubic
19

tanpa lib eksternal ...

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
sumber
15

Jika dilaksanakan Comparable<T>(ex. Integer, String, Date), Anda dapat melakukannya dengan menggunakan Comparator.reverseOrder().

List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.stream()
     .sorted(Comparator.reverseOrder())
     .forEach(System.out::println);
YujiSoftware
sumber
6
Ini tidak membalikkan arus. Ini memilah aliran dalam urutan terbalik. Jadi jika Anda memiliki Stream.of(1,3,2)hasilnya Stream.of(3,2,1)TIDAKStream.of(2,3,1)
wilmol
12

Anda bisa menentukan kolektor Anda sendiri yang mengumpulkan elemen-elemen dalam urutan terbalik:

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

Dan gunakan seperti:

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

Saya menggunakan ArrayList dalam urutan ke depan untuk secara efisien memasukkan kumpulkan item (di akhir daftar), dan Guava Lists.reverse untuk secara efisien memberikan tampilan daftar yang terbalik tanpa membuat salinannya lagi.

Berikut ini beberapa kasus uji untuk pengumpul khusus:

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);
    }
}
lingkup leksikal
sumber
9

cyclops- reacting StreamUtils memiliki metode Stream terbalik ( javadoc ).

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

Ia bekerja dengan mengumpulkan ke ArrayList dan kemudian menggunakan kelas ListIterator yang dapat beralih ke kedua arah, untuk beralih mundur ke belakang daftar.

Jika Anda sudah memiliki Daftar, itu akan lebih efisien

  StreamUtils.reversedStream(Arrays.asList("1", "2", "20", "3"))
             .forEach(System.out::println);
John McClean
sumber
1
Nice tidak tahu tentang proyek ini
vach
1
cyclops sekarang juga dilengkapi dengan Spliterators untuk pembalikan Stream yang efisien (saat ini untuk Rentang, array, dan Daftar). Membuat cyclops SequenceM extension Stream dengan SequenceM.of, SequenceM.range atau SequenceM.fromList akan secara otomatis memanfaatkan spliterator yang dapat dibalik secara efisien.
John McClean
6

Saya sarankan menggunakan jOOλ , ini adalah pustaka hebat yang menambahkan banyak fungsi berguna ke stream Java 8 dan lambdas.

Anda kemudian dapat melakukan hal berikut:

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

Sederhana seperti itu. Ini adalah perpustakaan yang cukup ringan, dan layak ditambahkan ke proyek Java 8.

Luken
sumber
5

Inilah solusi yang saya buat:

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

kemudian menggunakan pembanding tersebut:

IntStream.range(-range, 0).boxed().sorted(BY_DESCENDING_ORDER).forEach(// etc...
vach
sumber
2
Ini hanya jawaban untuk "pertanyaan spesifik" Anda tetapi tidak untuk "pertanyaan umum" Anda.
chiccodoro
9
Collections.reverseOrder()ada sejak Java 1.2 dan bekerja dengan Integer...
Holger
4

Bagaimana dengan metode utilitas ini?

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

Tampaknya bekerja dengan semua case tanpa duplikasi.

Jerome
sumber
Saya suka solusi ini. Sebagian besar jawaban dibagi menjadi dua kategori: (1) Balikkan koleksi dan .stream (), (2) Banding ke pengumpul khusus. Keduanya sama sekali tidak perlu. Kalau tidak, itu akan bersaksi tentang beberapa masalah ekspresi bahasa yang serius di JDK 8 itu sendiri. Dan jawaban Anda membuktikan sebaliknya :)
vitrums
4
List newStream = list.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList());
        newStream.forEach(System.out::println);
siddmuk2005
sumber
3

Cara termudah (pengumpulan sederhana - mendukung aliran paralel):

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

Cara lanjutan (mendukung aliran paralel secara berkelanjutan):

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

Perhatikan bahwa Anda dapat dengan cepat meluas ke jenis aliran lainnya (IntStream, ...).

Pengujian:

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

Hasil:

Six
Five
Four
Three
Two
One

Catatan tambahan: Itu simplest waytidak begitu berguna ketika digunakan dengan operasi aliran lainnya (join join merusak paralelisme). Tidak advance waymemiliki masalah itu, dan itu tetap juga karakteristik awal dari aliran, misalnya SORTED, dan jadi, itu cara untuk digunakan untuk menggunakan operasi aliran lain setelah kebalikannya.

Tet
sumber
2

Orang dapat menulis kolektor yang mengumpulkan elemen dalam urutan terbalik:

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

Dan gunakan seperti ini:

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

Jawaban asli (berisi bug - tidak berfungsi dengan benar untuk aliran paralel):

Metode reverse stream tujuan umum dapat terlihat seperti:

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

Bukan murni Java8 tetapi jika Anda menggunakan metode Lists.reverse () jambu dalam hubungannya, Anda dapat dengan mudah mencapai ini:

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

Berkenaan dengan pertanyaan spesifik menghasilkan kebalikan IntStream:

mulai dari Java 9 Anda dapat menggunakan versi tiga argumen dari 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

dimana:

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

  • seed - elemen awal;
  • hasNext - predikat untuk diterapkan pada elemen untuk menentukan kapan aliran harus berakhir;
  • next - suatu fungsi untuk diterapkan pada elemen sebelumnya untuk menghasilkan elemen baru.
Oleksandr Pyrohov
sumber
1

Untuk referensi saya melihat masalah yang sama, saya ingin bergabung dengan nilai string elemen aliran dalam urutan terbalik.

itemList = {terakhir, tengah, pertama} => pertama, tengah, terakhir

Saya mulai menggunakan koleksi perantara dengan collectingAndThendari comonad atau ArrayDequekolektor Stuart Marks , meskipun saya tidak senang dengan koleksi perantara, dan streaming lagi

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

Jadi saya mengulangi jawaban Stuart Marks yang menggunakan Collector.ofpabrik, yang memiliki lambda finisher yang menarik .

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

Karena dalam hal ini alirannya tidak paralel, penggabungnya tidak terlalu relevan, saya menggunakan insert tetap demi konsistensi kode tetapi tidak masalah karena akan tergantung pada pembuat string mana yang pertama kali dibangun.

Saya melihat StringJoiner, namun tidak memiliki insertmetode.

Brice
sumber
1

Menjawab pertanyaan spesifik tentang pembalikan dengan IntStream, di bawah ini berfungsi untuk saya:

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

ArrayDequelebih cepat di stack daripada Stack atau LinkedList. "push ()" memasukkan elemen di bagian depan Deque

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

Membalikkan string atau Array apa pun

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

split dapat dimodifikasi berdasarkan pembatas atau ruang

sai manoj
sumber
1

solusi paling sederhana adalah menggunakan List::listIteratordanStream::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
sumber
Layak untuk ditambahkan yang Stream.generate()menghasilkan aliran tak terbatas, jadi panggilan ke limit()sangat penting di sini.
andrebrait
0

Beginilah cara saya melakukannya.

Saya tidak suka ide membuat koleksi baru dan membalikkan iterasi.

Ide peta IntStream # cukup rapi, tapi saya lebih suka metode IntStream # iterate, karena saya pikir ide hitung mundur ke Zero lebih baik diungkapkan dengan metode iterate dan lebih mudah dipahami dalam hal berjalan array dari belakang ke depan.

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

Berikut ini beberapa tes untuk membuktikannya bekerja:

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
sumber
0

Dalam semua ini saya tidak melihat jawaban yang akan saya tuju terlebih dahulu.

Ini bukan jawaban langsung untuk pertanyaan itu, tetapi ini merupakan solusi potensial untuk masalah tersebut.

Bangun daftar di belakang. Jika Anda bisa, gunakan LinkedList sebagai ganti ArrayList dan saat Anda menambahkan item gunakan "Push" alih-alih menambahkan. Daftar akan dibuat dalam urutan terbalik dan kemudian akan mengalir dengan benar tanpa manipulasi.

Ini tidak akan cocok dengan kasus di mana Anda berurusan dengan array atau daftar primitif yang sudah digunakan dalam berbagai cara tetapi bekerja dengan baik dalam sejumlah kasus mengejutkan.

Bill K.
sumber
0

Metode ini berfungsi dengan Stream apa pun dan memenuhi persyaratan 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
sumber
-1

Cara paling umum dan termudah untuk membalik daftar adalah:

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

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

    }
parmeshwor11
sumber
4
Anda melanggar kontrak Comparator. Akibatnya, tidak ada yang dapat menjamin Anda bahwa "trik" ini akan berfungsi di versi Java apa pun di masa depan dengan algoritma pengurutan apa pun. Trik yang sama tidak berfungsi untuk aliran paralel, misalnya, karena algoritma pengurutan paralel menggunakan Comparatorcara yang berbeda. Untuk pengurutan berurutan itu berfungsi murni secara kebetulan. Saya tidak akan merekomendasikan siapa pun untuk menggunakan solusi ini.
Tagir Valeev
1
Juga tidak berfungsi saat Anda mengaturSystem.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Tagir Valeev
Saya pikir itu hanya tentang mencetak hal-hal dalam urutan terbalik bukan dalam urutan (mis. Urutan menurun), dan ia bekerja dengan aliran paralel juga: public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
parmeshwor11
1
Coba reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))(hasilnya mungkin tergantung pada jumlah core).
Tagir Valeev
-1

Java 8 cara untuk melakukan ini:

    List<Integer> list = Arrays.asList(1,2,3,4);
    Comparator<Integer> comparator = Integer::compare;
    list.stream().sorted(comparator.reversed()).forEach(System.out::println);
Antonio
sumber
4
Ini mengurutkan dalam urutan terbalik, bukan mengembalikan daftar.
Jochen Bedersdorfer