Di Java 8, bagaimana saya bisa memfilter koleksi menggunakan Stream
API dengan memeriksa perbedaan properti setiap objek?
Misalnya saya punya daftar Person
objek dan saya ingin menghapus orang dengan nama yang sama,
persons.stream().distinct();
Akan menggunakan pemeriksaan kesetaraan default untuk Person
objek, jadi saya perlu sesuatu seperti,
persons.stream().distinct(p -> p.getName());
Sayangnya distinct()
metode ini tidak memiliki kelebihan seperti itu. Tanpa memodifikasi pemeriksaan kesetaraan di dalam Person
kelas, mungkinkah melakukan ini dengan ringkas?
sumber
Function<? super T, ?>
, bukanFunction<? super T, Object>
. Juga harus dicatat bahwa untuk aliran paralel yang dipesan solusi ini tidak menjamin objek mana yang akan diekstraksi (tidak seperti normaldistinct()
). Juga untuk aliran berurutan ada overhead tambahan dalam menggunakan CHM (yang tidak ada dalam solusi @nosid). Akhirnya solusi ini melanggar kontrakfilter
metode yang predikatnya harus stateless seperti yang dinyatakan dalam JavaDoc. Namun demikian, diputuskan.distinctByKey
tidak tahu apakah itu digunakan dalam aliran paralel. Ini menggunakan CHM jika sedang digunakan secara paralel, meskipun ini menambahkan overhead dalam kasus berurutan seperti yang dicatat Tagir Valeev di atas.distinctByKey
. Tapi itu berfungsi jika Anda menelepondistinctByKey
setiap waktu, sehingga menciptakan contoh Predikat baru setiap kali..filter(distinctByKey(...))
. Ini akan mengeksekusi metode sekali dan mengembalikan predikat. Jadi pada dasarnya peta sudah digunakan kembali jika Anda menggunakannya dengan benar dalam aliran. Jika Anda membuat peta statis, peta akan dibagikan untuk semua penggunaan. Jadi jika Anda memiliki dua aliran menggunakan inidistinctByKey()
, keduanya akan menggunakan peta yang sama, yang bukan yang Anda inginkan.CallSite
akan dikaitkan denganget$Lambda
metode - yang akan mengembalikan contoh baruPredicate
sepanjang waktu, tetapi contoh-contoh itu akan berbagi samamap
danfunction
sejauh yang saya mengerti. Sangat bagus!Alternatifnya adalah dengan menempatkan orang-orang di peta menggunakan nama sebagai kunci:
Perhatikan bahwa Orang yang disimpan, dalam kasus duplikat nama, akan menjadi orang pertama yang dikonfigurasikan.
sumber
distinct()
tanpa overhead itu? Bagaimana setiap implementasi tahu jika ia telah melihat objek sebelumnya tanpa benar-benar mengingat semua nilai berbeda yang telah dilihatnya? Jadi overheadtoMap
dandistinct
sangat mungkin sama.distinct()
itu sendiri menciptakan.persons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();
TreeSet
) yang sudah berbeda pula atausorted
pada aliran yang juga menyangga semua elemen.Anda dapat membungkus objek orang ke dalam kelas lain, yang hanya membandingkan nama orang tersebut. Setelah itu, Anda membuka bungkusan benda yang dibungkus untuk membuat orang streaming lagi. Operasi aliran mungkin terlihat sebagai berikut:
Kelas
Wrapper
mungkin terlihat sebagai berikut:sumber
equals
Metode dapat disederhanakanreturn other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());
Solusi lain, menggunakan
Set
. Mungkin bukan solusi yang ideal, tetapi berhasilAtau jika Anda dapat mengubah daftar asli, Anda dapat menggunakan metode removeIf
sumber
Ada pendekatan yang lebih sederhana menggunakan TreeSet dengan pembanding kustom.
sumber
Kita juga dapat menggunakan RxJava ( pustaka ekstensi reaktif yang sangat kuat )
atau
sumber
Observable
berbasis push sedangkan berbasisStream
pull. stackoverflow.com/questions/30216979/…Flux.fromIterable(persons).distinct(p -> p.getName())
Stream
API", bukan "tidak harus menggunakan aliran". Yang mengatakan, ini adalah solusi bagus untuk masalah XY menyaring aliran ke nilai yang berbeda.Anda dapat menggunakan
groupingBy
kolektor:Jika Anda ingin memiliki aliran lain, Anda dapat menggunakan ini:
sumber
Anda dapat menggunakan
distinct(HashingStrategy)
metode ini di Eclipse Collections .Jika Anda bisa menolak
persons
untuk mengimplementasikan antarmuka Eclipse Collections, Anda dapat memanggil metode secara langsung pada daftar.HashingStrategy hanyalah antarmuka strategi yang memungkinkan Anda untuk menentukan implementasi kustom dengan equals dan hashcode.
Catatan: Saya pengendara untuk Eclipse Collections.
sumber
Saya sarankan menggunakan Vavr , jika Anda bisa. Dengan perpustakaan ini Anda dapat melakukan hal berikut:
sumber
Anda dapat menggunakan perpustakaan StreamEx :
sumber
String
terima kasih kepada string interning, tetapi juga mungkin tidak.Memperluas jawaban Stuart Marks, ini dapat dilakukan dengan cara yang lebih pendek dan tanpa peta bersamaan (jika Anda tidak membutuhkan aliran paralel):
Lalu hubungi:
sumber
Collections.synchronizedSet(new HashSet<>())
. Tetapi mungkin akan lebih lambat daripada denganConcurrentHashMap
.Pendekatan serupa yang digunakan Saeed Zarinfam tetapi lebih gaya Java 8 :)
sumber
flatMap(plans -> plans.stream().findFirst().stream())
itu menghindari penggunaan get on OptionalSaya membuat versi generik:
Contoh:
sumber
Pustaka lain yang mendukung ini adalah jOOλ , dan
Seq.distinct(Function<T,U>)
metodenya:Di bawah tenda , itu praktis sama dengan jawaban yang diterima .
sumber
sumber
Pendekatan saya untuk ini adalah untuk mengelompokkan semua objek dengan properti yang sama bersama, lalu potong pendek grup menjadi ukuran 1 dan akhirnya kumpulkan sebagai
List
.sumber
Daftar objek yang berbeda dapat ditemukan menggunakan:
sumber
Cara termudah untuk mengimplementasikan ini adalah dengan melompat pada fitur pengurutan karena sudah menyediakan opsi
Comparator
yang dapat dibuat menggunakan properti elemen. Maka Anda harus memfilter duplikat yang dapat dilakukan menggunakan statefullPredicate
yang menggunakan fakta bahwa untuk aliran yang diurutkan semua elemen yang sama berbatasan:Tentu saja, statefull
Predicate
bukan thread-safe, namun jika itu kebutuhan Anda, Anda dapat memindahkan logika ini keCollector
dan membiarkan aliran menjaga keamanan thread saat menggunakan AndaCollector
. Ini tergantung pada apa yang ingin Anda lakukan dengan aliran elemen berbeda yang tidak Anda beri tahu kami dalam pertanyaan Anda.sumber
Berdasarkan jawaban @ josketres, saya membuat metode utilitas umum:
Anda bisa menjadikan Java 8 ini lebih ramah dengan membuat Kolektor .
sumber
Mungkin akan bermanfaat bagi seseorang. Saya punya sedikit persyaratan lain. Memiliki daftar objek
A
dari pihak ke-3 menghapus semua yang memilikiA.b
bidang yang sama untuk yang samaA.id
(beberapaA
objek denganA.id
daftar yang sama ). Aliran partisi jawaban oleh Tagir Valeev menginspirasi saya untuk menggunakan customCollector
yang kembaliMap<A.id, List<A>>
. SederhanaflatMap
akan melakukan sisanya.sumber
Saya punya situasi, di mana saya seharusnya mendapatkan elemen yang berbeda dari daftar berdasarkan 2 kunci. Jika Anda ingin berbeda berdasarkan pada dua tombol atau mungkin tombol komposit, coba ini
sumber
Dalam kasus saya, saya perlu mengontrol apa elemen sebelumnya. Saya kemudian membuat Predikat stateful di mana saya mengontrol jika elemen sebelumnya berbeda dari elemen saat ini, dalam hal itu saya menyimpannya.
sumber
Solusi saya dalam daftar ini:
Dalam situasi saya, saya ingin menemukan nilai yang berbeda dan memasukkannya ke dalam Daftar.
sumber
Sementara jawaban tertinggi yang dipilih adalah jawaban terbaik untuk Java 8, namun pada saat yang sama benar-benar terburuk dalam hal kinerja. Jika Anda benar-benar menginginkan aplikasi berkinerja rendah yang buruk, silakan gunakan. Persyaratan sederhana untuk mengekstraksi seperangkat Nama Person yang unik harus dicapai hanya dengan "Untuk Setiap" dan "Set". Hal-hal menjadi lebih buruk jika daftar di atas ukuran 10.
Pertimbangkan Anda memiliki koleksi 20 Objek, seperti ini:
Di mana Anda keberatan
SimpleEvent
terlihat seperti ini:Dan untuk menguji, Anda memiliki JMH kode seperti ini, (Harap dicatat, im menggunakan yang sama distinctByKey Predikat disebutkan dalam jawaban diterima):
Maka Anda akan mendapatkan hasil Benchmark seperti ini:
Dan seperti yang Anda lihat, For-Each sederhana adalah 3 kali lebih baik dalam throughput dan skor kesalahan kurang dibandingkan dengan Java 8 Stream.
Semakin tinggi throughput, semakin baik kinerjanya
sumber
sumber
Jika Anda ingin Daftar Orang berikut ini akan menjadi cara sederhana
Selain itu, jika Anda ingin menemukan daftar nama yang berbeda atau unik , bukan Orang , Anda dapat menggunakan dua metode berikut juga.
Metode 1: menggunakan
distinct
Metode 2: menggunakan
HashSet
sumber
Person
s.Kode paling sederhana yang dapat Anda tulis:
sumber