Periksa apakah satu daftar berisi elemen dari yang lain

105

Saya memiliki dua daftar dengan objek berbeda di dalamnya.

List<Object1> list1;
List<Object2> list2;

Saya ingin memeriksa apakah elemen dari list1 ada di list2, berdasarkan atribut tertentu (Object1 dan Object2 memiliki (antara lain), satu atribut bersama (dengan tipe Long), bernama atributSame).

sekarang, saya melakukannya seperti ini:

boolean found = false;
for(Object1 object1 : list1){
   for(Object2 object2: list2){
       if(object1.getAttributeSame() == object2.getAttributeSame()){
           found = true;
           //also do something
       }
    }
    if(!found){
        //do something
    }
    found = false;
}

Tetapi menurut saya ada cara yang lebih baik dan lebih cepat untuk melakukan ini :) Dapatkah seseorang melamarnya?

Terima kasih!

Ned
sumber
pertama, saat Anda menyetel found = true; kemudian cukup istirahat; atau keluar dari lingkaran
jsist
stackoverflow.com/questions/5187888/… . Selain itu, untuk pencarian cepat, coba gunakan Pencarian Biner dan ubah DS Anda agar sesuai dengan situasi ...
jsist
apakah mereka memiliki orang tua yang sama selain Object?
Woot4Moo
@ Woot4Moo tidak, mereka tidak
Ned

Jawaban:

221

Jika Anda hanya perlu menguji persamaan dasar, ini dapat dilakukan dengan JDK dasar tanpa mengubah daftar masukan dalam satu baris

!Collections.disjoint(list1, list2);

Jika Anda perlu menguji properti tertentu, itu lebih sulit. Saya akan merekomendasikan, secara default,

list1.stream()
   .map(Object1::getProperty)
   .anyMatch(
     list2.stream()
       .map(Object2::getProperty)
       .collect(toSet())
       ::contains)

... yang mengumpulkan nilai-nilai yang berbeda list2dan menguji setiap nilai list1untuk keberadaannya.

Louis Wasserman
sumber
1
Bukankah ini akan selalu mengembalikan nilai salah karena keduanya adalah 2 objek yang berbeda?
Venki
2
Um, bukan? Tes terputus-putus jika tidak ada objek yang sama dengan () satu sama lain di antara dua koleksi.
Louis Wasserman
13
Juga, harap dicatat bahwa, untuk daftar, ini akan menjadi O (n * m); jika Anda ingin menyalin list1ke a Setsebelum membandingkan, Anda akan mendapatkan O (n) + O (m), yaitu, O (n + m), dengan biaya tambahan RAM; ini soal memilih antara kecepatan atau memori.
Haroldo_OK
Ini akan bekerja hanya jika "List <Person> list1; List <Person> list2" tetapi tidak pada dua objek atau tipe data yang berbeda seperti List <Person> list1; Daftar <Pekerjaan> list2.
whoami
Tentu saja ini tidak akan berhasil untuk skenario @Zephyr tersebut, untuk pertanyaan yang diajukan, ini berfungsi dengan sempurna selama Anda memiliki hak yang sama untuk diterapkan. itu yang terpenting!
Syed Siraj Uddin
38

Anda dapat menggunakan Apache Commons CollectionUtils :

if(CollectionUtils.containsAny(list1,list2)) {  
    // do whatever you want
} else { 
    // do other thing 
}  

Ini mengasumsikan bahwa Anda telah membebani dengan benar fungsionalitas yang sama untuk objek kustom Anda.

Woot4Moo
sumber
9
sudah 4 tahun dan saya juga secara eksplisit memanggil paket dan fungsinya.
Woot4Moo
1
Tidak suka apache commons saat ada solusi khusus jdk
ohcibi
9
@ohcibi Java juga memiliki logger bawaan, Anda harus menurunkan suara orang-orang yang menyarankan untuk menggunakan Log4j dan Log4j2 saat Anda menggunakannya.
Woot4Moo
1
@ Woot4Moo itu tergantung. Tidak ada alasan untuk downvote jika ada alasan untuk menggunakan Log4j untuk menyelesaikan masalah OP. Dalam hal ini apache commons akan menjadi bloat yang tidak berguna seperti pada 99% jawaban yang menyarankan apache commons.
ohcibi
1
@ohcibi tetapi jika Anda sudah menggunakan Apache commons maka tidak terlalu mengasapi. Ini jawaban yang bagus.
vab2048
20

Untuk mempersingkat logika Narendra, Anda dapat menggunakan ini:

boolean var = lis1.stream().anyMatch(element -> list2.contains(element));
ketanjain
sumber
3
jawaban ini kurang dihargai.
Kervvv
1
Anda dapat mempersingkatnya sedikit lagi jika Anda ingin:list1.stream().anyMatch(list2::contains);
tarka
9

Ada salah satu metode dari Collectionbernama retainAlltetapi memiliki beberapa efek samping untuk Anda referensi

Mempertahankan hanya elemen dalam daftar ini yang terdapat dalam koleksi yang ditentukan (operasi opsional). Dengan kata lain, menghapus dari daftar ini semua elemennya yang tidak terdapat dalam koleksi yang ditentukan.

benar jika daftar ini berubah sebagai akibat dari panggilan tersebut

Itu seperti

boolean b = list1.retainAll(list2);
Harmeet Singh
sumber
5

Jawaban Loius benar, saya hanya ingin menambahkan contoh:

listOne.add("A");
listOne.add("B");
listOne.add("C");

listTwo.add("D");
listTwo.add("E");
listTwo.add("F");      

boolean noElementsInCommon = Collections.disjoint(listOne, listTwo); // true
Matias Elorriaga
sumber
1
Saya pikir jika Anda menambahkan elemen 'A' ke listTwo.add daftar kedua ("A"); meskipun Collections.disjoint (listOne, listTwo); mengembalikan true.
Sairam Kukadala
2

cara yang lebih cepat akan membutuhkan ruang tambahan.

Sebagai contoh:

  1. letakkan semua item dalam satu daftar ke dalam HashSet (Anda harus mengimplementasikan fungsi hash sendiri untuk menggunakan object.getAttributeSame ())

  2. Pergi melalui daftar lain dan periksa apakah ada item di HashSet.

Dengan cara ini setiap objek dikunjungi paling banyak sekali. dan HashSet cukup cepat untuk memeriksa atau memasukkan objek apa pun di O (1).

lavin
sumber
2

Menurut JavaDoc untuk .contains(Object obj):

Mengembalikan nilai benar jika daftar ini berisi elemen yang ditentukan. Secara lebih formal, mengembalikan nilai true jika dan hanya jika daftar ini berisi setidaknya satu elemen e sehingga (o == null? E == null: o.equals (e)).

Jadi jika Anda mengganti .equals()metode Anda untuk objek yang Anda berikan, Anda seharusnya bisa melakukan:if(list1.contains(object2))...

Jika elemen akan unik (mis. Memiliki atribut berbeda) Anda dapat menimpa .equals()dan .hashcode()dan menyimpan semuanya di HashSets. Ini akan memungkinkan Anda untuk memeriksa apakah satu berisi elemen lain dalam waktu yang konstan.

npinti
sumber
2

untuk membuatnya lebih cepat, Anda bisa menambahkan jeda; dengan cara itu loop akan berhenti jika ditemukan disetel ke true:

boolean found = false;
for(Object1 object1 : list1){
   for(Object2 object2: list2){
       if(object1.getAttributeSame() == object2.getAttributeSame()){
           found = true;
           //also do something  
           break;
       }
    }
    if(!found){
        //do something
    }
    found = false;
}

Jika Anda ingin memiliki peta sebagai pengganti daftar dengan kunci atributSame, Anda dapat memeriksa lebih cepat untuk nilai dalam satu peta jika ada nilai yang sesuai di peta kedua atau tidak.

Tom
sumber
Hai Tom, terima kasih telah memperhatikan! Ya, saya lupa "istirahat" saat mengetik. Tetapi saya berpikir mungkin ada beberapa algoritma, atau saya harus mengubah daftar ini menjadi beberapa koleksi lain.
Ned
tidak ada yang lebih baik dari O (n * m)?
Woot4Moo
.getAttributeSame ()?
Maveň ツ
Implementasi untuk metode getAttributeSame () dari Object1 dan Object2 tidak disediakan, tetapi juga tidak relevan untuk pertanyaan dan jawaban; itu hanya mengembalikan atribut (attributeSame, a Long) yang dimiliki kedua kelas.
Tom
0

Bisakah Anda menentukan jenis data yang Anda pegang? apakah ini data besar? apakah sudah diurutkan? Saya pikir Anda perlu mempertimbangkan pendekatan efisiensi yang berbeda tergantung pada datanya.

Misalnya, jika data Anda besar dan tidak diurutkan, Anda dapat mencoba dan mengulang kedua daftar bersama-sama dengan index dan menyimpan setiap atribut list di list helper lainnya. lalu Anda dapat melakukan pemeriksaan silang dengan atribut saat ini di daftar pembantu.

semoga berhasil

diedit: dan saya tidak akan merekomendasikan overloading sama. itu berbahaya dan mungkin bertentangan dengan makna objek Anda.

REL
sumber
0

org.springframework.util.CollectionUtils

boolean containsAny(java.util.Collection<?> source, java.util.Collection<?> candidates)

Return true if any element in 'candidates' is contained in 'source'; otherwise returns false
akjain
sumber
0

Dengan java 8, kita bisa melakukan seperti di bawah ini untuk memeriksa apakah satu daftar berisi elemen daftar lainnya

boolean var = lis1.stream().filter(element -> list2.contains(element)).findFirst().isPresent();
Narendra Jaggi
sumber
0

Jika Anda ingin memeriksa apakah suatu elemen ada dalam daftar, gunakan metode berisi.

if (list1.contains(Object o))
{
   //do this
}
Fakipo
sumber