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:
IntStream
menyediakan 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 IntStream
saya akan mendapatkan kesalahan kompilator ini
Kesalahan: (191, 0) ajc: Metode
sorted()
dalam tipeIntStream
ini tidak berlaku untuk argumen (Integer::compare
)
Apa yang kulewatkan di sini?
IntStream
tidak memiliki.sorted(Comparator)
metode; Anda harus pergi melaluiStream<Integer>
pertama dan reverse sana sebelum menghasilkan sebuahIntStream
IntStream.range(0, n)
urutan terbalik, lakukan sesuatu sepertimap(i -> n - i - 1)
. Tidak perlu melakukan tinju dan sortir.1, 3, 2
, apa hasil yang Anda harapkan? Apakah Anda ingin seperti aliran terbalik2, 3, 1
atau aliran yang disortir3, 2, 1
?Jawaban:
Untuk pertanyaan spesifik tentang menghasilkan kebalikan
IntStream
, coba sesuatu seperti ini: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.
Teknik lain menggunakan kolektor untuk mengumpulkan item ke dalam daftar terbalik. Ini melakukan banyak penyisipan di bagian depan
ArrayList
objek, jadi ada banyak penyalinan yang terjadi.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
ArrayDeque
sebaliknya, yang secara efisien mendukung penyisipan di bagian depan. Kerutan kecil adalah bahwa kita tidak dapat menggunakan bentuk tiga-argStream.collect()
; itu membutuhkan isi dari arg kedua digabungkan ke dalam arg pertama, dan tidak ada operasi massal "add-all-at-front" aktifDeque
. Sebagai gantinya, kita gunakanaddAll()
untuk menambahkan konten dari arg pertama ke akhir yang kedua, dan kemudian kita mengembalikan yang kedua. Ini mengharuskan menggunakanCollector.of()
metode pabrik.Kode lengkapnya adalah ini:
Hasilnya adalah
Deque
bukannyaList
, tetapi itu seharusnya tidak menjadi masalah, karena dapat dengan mudah diulangi atau dialirkan dalam urutan yang sekarang terbalik.sumber
IntStream.iterate(to-1, i->i-1).limit(to-from)
.limit(endExcl-(long)startIncl)
sebagai gantinya, tetapi untuk aliran besar seperti itu, sangat tidak dianjurkan karena jauh lebih efisien daripadarange
solusi berbasis. Pada saat saya menulis komentar, saya tidak mengetahui perbedaan efisiensi.Solusi elegan
sumber
Comparable
...Banyak solusi di sini mengurutkan atau membalikkan
IntStream
, tetapi itu tidak perlu memerlukan penyimpanan perantara. Solusi Stuart Marks adalah caranya:Itu benar menangani overflow juga, melewati tes ini:
sumber
Estreams
nama (saya akan menghapusnya dari pos). Ini salah satu kelas utilitas internal yang perusahaan kami, yang kami gunakan untuk suplemenjava.util.stream.Stream
'sstatic
metode.StreamEx
menentukan langkah:IntStreamEx.rangeClosed(from-1, to, -1)
Pertanyaan Umum:
Stream tidak menyimpan elemen apa pun.
Jadi elemen iterasi dalam urutan terbalik tidak mungkin tanpa menyimpan elemen dalam beberapa koleksi perantara.
Pembaruan: Mengubah LinkedList menjadi ArrayDeque (lebih baik) lihat di sini untuk detailnya
Cetakan:
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 )
sumber
sorted
dandistinct
benar - benar menyimpan hasil antara. Lihat paket API docs untuk beberapa informasi tentang itu.No storage
di halaman yang sama. Bahkan toko itu menyimpan kita tidak bisa mendapatkan akses ke penyimpanan itu (No storage
saya kira baik-baik saja)tanpa lib eksternal ...
sumber
Jika dilaksanakan
Comparable<T>
(ex.Integer
,String
,Date
), Anda dapat melakukannya dengan menggunakanComparator.reverseOrder()
.sumber
Stream.of(1,3,2)
hasilnyaStream.of(3,2,1)
TIDAKStream.of(2,3,1)
Anda bisa menentukan kolektor Anda sendiri yang mengumpulkan elemen-elemen dalam urutan terbalik:
Dan gunakan seperti:
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:
sumber
cyclops- reacting StreamUtils memiliki metode Stream terbalik ( javadoc ).
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
sumber
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:
Sederhana seperti itu. Ini adalah perpustakaan yang cukup ringan, dan layak ditambahkan ke proyek Java 8.
sumber
Inilah solusi yang saya buat:
kemudian menggunakan pembanding tersebut:
sumber
Collections.reverseOrder()
ada sejak Java 1.2 dan bekerja denganInteger
...Bagaimana dengan metode utilitas ini?
Tampaknya bekerja dengan semua case tanpa duplikasi.
sumber
sumber
Cara termudah (pengumpulan sederhana - mendukung aliran paralel):
Cara lanjutan (mendukung aliran paralel secara berkelanjutan):
Perhatikan bahwa Anda dapat dengan cepat meluas ke jenis aliran lainnya (IntStream, ...).
Pengujian:
Hasil:
Catatan tambahan: Itu
simplest way
tidak begitu berguna ketika digunakan dengan operasi aliran lainnya (join join merusak paralelisme). Tidakadvance way
memiliki masalah itu, dan itu tetap juga karakteristik awal dari aliran, misalnyaSORTED
, dan jadi, itu cara untuk digunakan untuk menggunakan operasi aliran lain setelah kebalikannya.sumber
Orang dapat menulis kolektor yang mengumpulkan elemen dalam urutan terbalik:
Dan gunakan seperti ini:
Jawaban asli (berisi bug - tidak berfungsi dengan benar untuk aliran paralel):
Metode reverse stream tujuan umum dapat terlihat seperti:
sumber
Bukan murni Java8 tetapi jika Anda menggunakan metode Lists.reverse () jambu dalam hubungannya, Anda dapat dengan mudah mencapai ini:
sumber
Berkenaan dengan pertanyaan spesifik menghasilkan kebalikan
IntStream
:mulai dari Java 9 Anda dapat menggunakan versi tiga argumen dari
IntStream.iterate(...)
: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.sumber
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
collectingAndThen
dari comonad atauArrayDeque
kolektor Stuart Marks , meskipun saya tidak senang dengan koleksi perantara, dan streaming lagiJadi saya mengulangi jawaban Stuart Marks yang menggunakan
Collector.of
pabrik, yang memiliki lambda finisher yang menarik .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
insert
metode.sumber
Menjawab pertanyaan spesifik tentang pembalikan dengan IntStream, di bawah ini berfungsi untuk saya:
sumber
ArrayDeque
lebih cepat di stack daripada Stack atau LinkedList. "push ()" memasukkan elemen di bagian depan Dequesumber
Membalikkan string atau Array apa pun
split dapat dimodifikasi berdasarkan pembatas atau ruang
sumber
solusi paling sederhana adalah menggunakan
List::listIterator
danStream::generate
sumber
Stream.generate()
menghasilkan aliran tak terbatas, jadi panggilan kelimit()
sangat penting di sini.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.
Berikut ini beberapa tes untuk membuktikannya bekerja:
sumber
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.
sumber
Metode ini berfungsi dengan Stream apa pun dan memenuhi persyaratan Java 8:
sumber
Cara paling umum dan termudah untuk membalik daftar adalah:
sumber
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 menggunakanComparator
cara yang berbeda. Untuk pengurutan berurutan itu berfungsi murni secara kebetulan. Saya tidak akan merekomendasikan siapa pun untuk menggunakan solusi ini.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); }
reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))
(hasilnya mungkin tergantung pada jumlah core).Java 8 cara untuk melakukan ini:
sumber