Saya memiliki dua ArrayList
tipe Answer
(kelas buatan sendiri).
Saya ingin membandingkan dua daftar untuk melihat apakah mereka berisi konten yang sama, tetapi tanpa masalah urutan.
Contoh:
//These should be equal.
ArrayList<String> listA = {"a", "b", "c"}
ArrayList<String> listB = {"b", "c", "a"}
List.equals
menyatakan bahwa dua daftar sama jika mengandung ukuran, isi, dan urutan elemen yang sama. Saya menginginkan hal yang sama, tetapi tanpa ketertiban.
Apakah ada cara sederhana untuk melakukan hal ini? Atau apakah saya perlu melakukan nested for loop, dan secara manual memeriksa setiap indeks dari kedua daftar?
Catatan: Saya tidak bisa mengubahnya dari ArrayList
jenis daftar lain, mereka harus tetap seperti itu.
Jawaban:
Anda bisa mengurutkan kedua daftar menggunakan
Collections.sort()
dan kemudian menggunakan metode equals. Solusi yang sedikit lebih baik adalah dengan pertama memeriksa apakah mereka memiliki panjang yang sama sebelum memesan, jika mereka tidak, maka mereka tidak sama, lalu mengurutkan, lalu gunakan sama. Misalnya, jika Anda memiliki dua daftar Strings, itu akan menjadi seperti:sumber
Collections.sort
halnya) - yaitu meneruskan salinan.one = new ArrayList<String>(one); two = new ArrayList<String>(two);
untuk menghindari merusak argumen.if(one == null || two == null || one.size() != two.size()){ return false; }
karena Anda sudah memeriksa apakah satu dan dua adalah nullMungkin cara termudah untuk daftar mana pun adalah:
sumber
[a, b, c]
dan[c, b, a, b]
dianggap memiliki konten yang sama. Jawaban ini akan mengatakan bahwa mereka melakukannya, tetapi bisa jadi untuk OP mereka tidak (karena satu berisi duplikat dan yang lainnya tidak). Untuk tidak mengatakan masalah efisiensi.Apache Commons Collections untuk menyelamatkan sekali lagi:
Docs:
sumber
false
? Lihat jawaban saya.implementation 'org.apache.commons:commons-collections4:4.3'
telah meninggalkan saya denganCaused by: com.android.builder.dexing.DexArchiveBuilderException: Failed to process
jalur kesalahan .sumber
count
menjadi negatif, menyederhanakan tubuh loop keif(!counts.containsKey(item) || --counts.get(item).count < 0) return false;
Juga, loop ke-3 dapat disederhanakan menjadifor(Count c: counts.values()) if(c.count != 0) return false;
counts
kosong"), tetapi tidak ingin mengaburkan poin utama: bahwa menggunakan peta pada dasarnya mengubah ini menjadi masalah O (N + M), dan merupakan dorongan tunggal terbesar yang mungkin Anda dapatkan.Map.merge
bilangan bulat kotak baru mungkin lebih sederhana dan lebih efisien untuk sebagian besar kasus penggunaan. Lihat juga jawaban ini ...Saya akan mengatakan jawaban ini melewatkan tipuan.
Bloch, dalam Java Efektifnya yang esensial, luar biasa, dan ringkas , mengatakan, dalam item 47, judul "Ketahui dan gunakan perpustakaan", "Untuk meringkas, jangan menemukan kembali roda". Dan dia memberikan beberapa alasan yang sangat jelas mengapa tidak.
Ada beberapa jawaban di sini yang menyarankan metode-metode dari
CollectionUtils
dalam pustaka Apache Commons Collections tetapi tidak ada yang menemukan cara paling indah dan elegan untuk menjawab pertanyaan ini :Penyebab : yaitu elemen-elemen yang tidak umum untuk keduanya
Lists
. Menentukan pelaku mana yang menjadi miliklist1
dan yang manalist2
relatif mudah menggunakanCollectionUtils.intersection( list1, culprits )
danCollectionUtils.intersection( list2, culprits )
.Namun itu cenderung berantakan dalam kasus-kasus seperti {"a", "a", "b"}
disjunction
dengan {"a", "b", "b"} ... kecuali ini bukan merupakan kegagalan dari perangkat lunak, tetapi melekat pada sifat seluk-beluk / ambiguitas tugas yang diinginkan.Anda selalu dapat memeriksa kode sumber (l. 287) untuk tugas seperti ini, seperti yang diproduksi oleh para insinyur Apache. Salah satu manfaat menggunakan kode mereka adalah bahwa kode tersebut telah dicoba dan diuji secara menyeluruh, dengan banyak kasus tepi dan kasus yang diantisipasi dan ditangani. Anda dapat menyalin dan mengubah kode ini ke isi hati Anda jika perlu.
NB Saya awalnya kecewa karena tidak ada
CollectionUtils
metode yang menyediakan versi overload yang memungkinkan Anda untuk memaksakan sendiriComparator
(sehingga Anda dapat mendefinisikan kembaliequals
sesuai dengan tujuan Anda).Tapi dari collections4 4.0 ada kelas baru,
Equator
yang "menentukan kesetaraan antara objek tipe T". Pada pemeriksaan kode sumber collections4 CollectionUtils.java mereka tampaknya menggunakan ini dengan beberapa metode, tetapi sejauh yang saya bisa tahu ini tidak berlaku untuk metode di bagian atas file, menggunakanCardinalityHelper
kelas ... yang termasukdisjunction
danintersection
.Saya menduga bahwa orang-orang Apache belum sempat karena ini tidak sepele: Anda harus membuat sesuatu seperti kelas "AbstractEquatingCollection", yang alih-alih menggunakan elemen bawaannya
equals
danhashCode
metode harus menggunakan mereka dariEquator
untuk semua metode dasar, sepertiadd
,contains
, dll NB bahkan ketika Anda melihat kode sumber,AbstractCollection
tidak melaksanakanadd
, juga tidak subclass abstrak sepertiAbstractSet
... Anda harus menunggu sampai kelas beton sepertiHashSet
danArrayList
sebelumadd
diimplementasikan. Cukup sakit kepala.Sementara itu perhatikan ruang ini, kurasa. Solusi sementara yang jelas adalah membungkus semua elemen Anda dalam kelas pembungkus dipesan lebih dahulu yang menggunakan
equals
danhashCode
untuk menerapkan jenis kesetaraan yang Anda inginkan ... kemudian memanipulasiCollections
objek pembungkus ini.sumber
Jika kardinalitas item tidak masalah (artinya: elemen berulang dianggap sebagai satu), maka ada cara untuk melakukan ini tanpa harus mengurutkan:
boolean result = new HashSet<>(listA).equals(new HashSet<>(listB));
Ini akan membuat
Set
dari masing-masingList
, dan kemudian menggunakanHashSet
'sequals
metode yang (tentu saja) mengabaikan pemesanan.Jika kardinalitas penting, maka Anda harus membatasi diri pada fasilitas yang disediakan oleh
List
; Jawaban @ jschoen akan lebih pas dalam kasus itu.sumber
Mengubah daftar ke Multiset Guava bekerja sangat baik. Mereka dibandingkan terlepas dari pesanan mereka dan elemen duplikat diperhitungkan juga.
sumber
Ini didasarkan pada solusi @cHao. Saya menyertakan beberapa perbaikan dan peningkatan kinerja. Ini berjalan kira-kira dua kali lebih cepat dari solusi sama dengan yang dipesan. Bekerja untuk semua jenis koleksi. Koleksi kosong dan null dianggap sama. Gunakan untuk keuntungan Anda;)
sumber
false
ketika kunci tidak ada atau hitungnya menjadi negatif. Karena ukuran total kedua daftar cocok (ini telah diperiksa di muka), tidak mungkin untuk memiliki nilai bukan nol setelah loop kedua, karena tidak mungkin ada nilai positif untuk kunci tanpa nilai negatif untuk kunci lain.Pikirkan bagaimana Anda akan melakukan ini sendiri, tidak ada komputer atau bahasa pemrograman. Saya memberi Anda dua daftar elemen, dan Anda harus memberi tahu saya jika mereka mengandung elemen yang sama. Bagaimana Anda melakukannya?
Salah satu pendekatan, seperti yang disebutkan di atas, adalah untuk mengurutkan daftar dan kemudian pergi elemen-elemen untuk melihat apakah mereka sama (yang adalah apa
List.equals
artinya). Ini berarti Anda diizinkan mengubah daftar atau diizinkan menyalinnya - dan tanpa mengetahui tugasnya, saya tidak dapat mengetahui apakah salah satu dari keduanya diizinkan.Pendekatan lain adalah melalui setiap daftar, menghitung berapa kali setiap elemen muncul. Jika kedua daftar memiliki jumlah yang sama di akhir, mereka memiliki elemen yang sama. Kode untuk itu adalah menerjemahkan setiap daftar ke peta
elem -> (# of times the elem appears in the list)
lalu memanggilequals
kedua peta. Jika peta-peta itu adaHashMap
, masing-masing terjemahan itu adalah operasi O (N), seperti perbandingannya. Itu akan memberi Anda algoritma yang cukup efisien dalam hal waktu, dengan biaya beberapa memori tambahan.sumber
Saya memiliki masalah yang sama dan menghasilkan solusi yang berbeda. Yang ini juga berfungsi ketika duplikat terlibat:
Keuntungan dibandingkan dengan beberapa solusi lain:
[1,2,3,3]
dan array lain,[1,2,2,3]
sebagian besar solusi di sini memberi tahu Anda bahwa mereka sama ketika tidak mempertimbangkan pesanan. Solusi ini menghindari ini dengan menghapus elemen yang sama dari daftar sementara;equals
) dan bukan referensi persamaan (==
);implement Comparable
agar solusi ini berfungsi.sumber
Jika Anda tidak berharap untuk mengurutkan koleksi dan Anda membutuhkan hasil bahwa ["A" "B" "C"] tidak sama dengan ["B" "B" "A" "C"],
tidak cukup, Anda perlu memeriksa ukurannya juga:
sumber
Solusi yang memanfaatkan metode pengurangan CollectionUtils:
sumber
Jika Anda peduli tentang pesanan, maka gunakan metode sama dengan:
Jika Anda tidak peduli memesan maka gunakan ini
sumber
Yang terbaik dari kedua dunia [@DiddiZ, @Chalkos]: yang ini terutama dibangun berdasarkan metode @Chalkos, tetapi memperbaiki bug (ifst.next ()), dan meningkatkan pemeriksaan awal (diambil dari @DiddiZ) serta menghilangkan kebutuhan untuk salin koleksi pertama (cukup hapus item dari salinan koleksi kedua).
Tidak memerlukan fungsi hashing atau penyortiran, dan memungkinkan awal ada pada ketidaksetaraan, ini adalah implementasi yang paling efisien. Itu kecuali Anda memiliki panjang koleksi dalam ribuan atau lebih, dan fungsi hashing yang sangat sederhana.
sumber
Ini adalah cara alternatif untuk memeriksa persamaan daftar array yang dapat berisi nilai nol:
sumber
Solusi saya untuk ini. Itu tidak begitu keren, tetapi bekerja dengan baik.
sumber
Dalam hal ini daftar {"a", "b"} dan {"b", "a"} sama. Dan {"a", "b"} dan {"b", "a", "c"} tidak sama. Jika Anda menggunakan daftar objek kompleks, ingatlah untuk menimpa metode sama dengan , seperti berisi semua menggunakannya di dalam.
sumber
AbstractCollection.containsAll()
. Anda harus mengizinkan untuk memiliki elemen duplikat seperti yang kita bicarakanLists
, bukan tentangSets
. Tolong lihat jawaban saya.