Sesuatu seperti 'berisi' untuk set Java?


307

Saya memiliki dua set, A dan B, dari jenis yang sama.

Saya harus mencari apakah A mengandung elemen apa pun dari set B.

Apa yang akan menjadi cara terbaik untuk melakukan itu tanpa mengulangi set? Perpustakaan Set memiliki contains(object)dan containsAll(collection), tetapi tidak containsAny(collection).


4
Apakah Anda mencoba menghindari iterasi untuk alasan efisiensi, atau untuk kebersihan kode?
yshavit

Jawaban:


527

Tidak akan Collections.disjoint(A, B)bekerja Dari dokumentasi:

Mengembalikan truejika dua koleksi yang ditentukan tidak memiliki elemen yang sama.

Dengan demikian, metode kembali falsejika koleksi berisi elemen umum.


17
Lebih suka ini daripada solusi lain karena tidak mengubah salah satu set atau membuat yang baru.
devconsole

7
Dan JRE standar, dan bekerja dengan Koleksi apa pun, tidak hanya ditetapkan.
Pierre Henry

4
Saya tidak berpikir ini tercepat, itu tidak akan terjadi hubungan pendek ketika elemen pertama dari persimpangan ditemukan.
Ben Horner

7
Sebenarnya itu akan korsleting segera setelah menemukan elemen umum pertama
Xipo


156

Stream::anyMatch

Karena Java 8 bisa Anda gunakan Stream::anyMatch.

setA.stream().anyMatch(setB::contains)

1
Ini persis apa yang saya cari! Terima kasih :-) Saya juga tidak tahu Anda bisa menggunakan variabel dengan sintaks ::!
dantiston

1
@vertvert, bisakah Anda menjelaskan apa yang terjadi di dalam anyMatch?
Cristiano

8
@Cristiano di sini, anyMatchakan mengalirkan semua elemen dari setAdan memanggil setB.contains()mereka semua. Jika "benar" dikembalikan untuk salah satu elemen, ekspresi secara keseluruhan akan dinilai benar. Semoga ini bisa membantu.
Alex Vulaj


31

Cara yang baik untuk mengimplementasikan containAny untuk set menggunakan Guava Sets.intersection () .

containsAnyakan mengembalikan boolean, sehingga panggilannya terlihat seperti:

Sets.intersection(set1, set2).isEmpty()

Ini mengembalikan true jika set terpisah, jika tidak palsu. Kompleksitas waktu ini kemungkinan sedikit lebih baik daripada mempertahankan semua karena Anda tidak perlu melakukan kloning untuk menghindari mengubah set asli Anda.


3
Satu-satunya kelemahan menggunakan pendekatan ini adalah Anda harus memasukkan perpustakaan jambu. Yang menurut saya tidak merugikan karena API pengumpulan google sangat kuat.
Mohammad Adnan

@DidierL sebagian besar fungsi utilitas Koleksi Guava, termasuk yang ini, mengembalikan tampilan struktur data. Jadi tidak ada "membangun set" yang perlu dikhawatirkan dalam kasus ini. Implementasinya menarik untuk dibaca di sini, dan / atau lihat javadoc: google.github.io/guava/releases/21.0/api/docs/com/google/common/…
chut

@MohammadAdnan Kerugian lain adalah ia menghitung persimpangan penuh - jika set1 dan set2 sangat besar, ini akan jauh lebih banyak sumber daya intensif (baik CPU dan memori bijaksana) daripada hanya memeriksa apakah mereka memiliki item yang sama.
Marxama


16

Saya menggunakan org.apache.commons.collections.CollectionUtils

CollectionUtils.containsAny(someCollection1, someCollection2)

Itu semuanya! Mengembalikan nilai true jika setidaknya satu elemen ada di kedua koleksi.

Mudah digunakan, dan nama fungsinya lebih sugestif.


5

Gunakan retainAll()di antarmuka Set. Metode ini menyediakan persimpangan elemen yang umum di kedua set. Lihat dokumen API untuk informasi lebih lanjut.


Jika titik menghindari iterasi adalah untuk efisiensi, retainAllmungkin tidak akan membantu. Implementasinya di AbstractCollectioniterates.
yshavit

1
yshavit benar. Mengingat bahwa OP sedang mencari untuk melihat apakah ada elemen di kedua set, algoritma yang tepat akan memiliki O(1)waktu berjalan dalam kasus terbaik, sedangkan retainAllakan memiliki sesuatu di sepanjang garis dari O(N)(itu akan tergantung pada ukuran hanya 1 set) waktu berjalan terbaik.
Zéychin

3

Saya akan merekomendasikan membuat HashMapdari himpunan A, dan kemudian iterasi melalui himpunan B dan memeriksa apakah ada unsur B dalam A. Ini akan berjalan dalam O(|A|+|B|)waktu (karena tidak akan ada tabrakan), sedangkan retainAll(Collection<?> c)harus berjalan dalam O(|A|*|B|)waktu.


3

Ada sedikit metode kasar untuk melakukan itu. Jika dan hanya jika set A berisi beberapa elemen B daripada panggilan

A.removeAll(B)

akan mengubah set A. Dalam situasi ini removeAll akan mengembalikan true (Seperti yang dinyatakan pada removeAll docs ). Tetapi mungkin Anda tidak ingin memodifikasi set A sehingga Anda mungkin berpikir untuk bertindak pada salinan, seperti ini:

new HashSet(A).removeAll(B)

dan nilai yang dikembalikan akan benar jika set tidak berbeda, yaitu mereka memiliki persimpangan non-kosong.

Juga lihat Koleksi Apache Commons


2

Anda dapat menggunakan metode retainAll dan mendapatkan persimpangan dua set Anda.


Dalam sebagian besar kasus, seseorang harus menyimpan set asli, jadi untuk menggunakannya retainAllperlu membuat salinan dari set asli. Maka itu lebih efisien untuk digunakan HashSetseperti yang disarankan oleh Zéychin .
Petr Pudlák

Itu perubahan kondisi, bukan pemeriksaan kondisi
Ben
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.