Saya ingin membandingkan dua koleksi (dalam C #), tapi saya tidak yakin cara terbaik untuk mengimplementasikan ini secara efisien.
Saya telah membaca utas lainnya tentang Enumerable.SequenceEqual , tapi bukan itu yang saya cari.
Dalam kasus saya, dua koleksi akan sama jika keduanya berisi item yang sama (tidak peduli urutannya).
Contoh:
collection1 = {1, 2, 3, 4};
collection2 = {2, 4, 1, 3};
collection1 == collection2; // true
Apa yang biasanya saya lakukan adalah untuk mengulang setiap item dari satu koleksi dan melihat apakah ada di koleksi lain, kemudian loop melalui setiap item dari koleksi lain dan melihat apakah ada di koleksi pertama. (Saya mulai dengan membandingkan panjangnya).
if (collection1.Count != collection2.Count)
return false; // the collections are not equal
foreach (Item item in collection1)
{
if (!collection2.Contains(item))
return false; // the collections are not equal
}
foreach (Item item in collection2)
{
if (!collection1.Contains(item))
return false; // the collections are not equal
}
return true; // the collections are equal
Namun, ini tidak sepenuhnya benar, dan itu mungkin bukan cara yang paling efisien untuk membandingkan dua koleksi untuk kesetaraan.
Contoh yang bisa saya pikirkan adalah salah:
collection1 = {1, 2, 3, 3, 4}
collection2 = {1, 2, 2, 3, 4}
Yang akan sama dengan implementasi saya. Haruskah saya menghitung berapa kali setiap item ditemukan dan memastikan bahwa jumlahnya sama di kedua koleksi?
Contoh-contohnya ada dalam semacam C # (sebut saja pseudo-C #), tetapi berikan jawaban Anda dalam bahasa apa pun yang Anda inginkan, itu tidak masalah.
Catatan: Saya menggunakan bilangan bulat dalam contoh untuk kesederhanaan, tetapi saya ingin dapat menggunakan objek tipe referensi juga (mereka tidak berperilaku benar sebagai kunci karena hanya referensi objek dibandingkan, bukan konten).
sumber
Jawaban:
Ternyata Microsoft sudah membahas hal ini dalam kerangka pengujiannya: CollectionAssert.AreEquivalent
Menggunakan reflektor, saya memodifikasi kode di belakang AreEquivalent () untuk membuat pembanding kesetaraan yang sesuai. Ini lebih lengkap daripada jawaban yang ada, karena memperhitungkan nol, mengimplementasikan IEqualityComparer dan memiliki beberapa efisiensi dan pemeriksaan tepi kasus. plus, ini Microsoft :)
Penggunaan sampel:
Atau jika Anda hanya ingin membandingkan dua koleksi secara langsung:
Akhirnya, Anda dapat menggunakan pembanding kesetaraan pilihan Anda:
sumber
EqualityComparer
(baik yang Anda berikan atauEqualityComparer.Default
, Anda dapat memeriksa Reflektor atau sumber referensi untuk memverifikasi ini). Benar, jika objek berubah (dan khususnya kode hash mereka berubah) saat metode ini berjalan maka hasilnya tidak terduga, tetapi itu hanya berarti metode ini tidak aman dalam konteks ini.EqualityComparer
(atauEqualityComparer.Default
jika tidak ada yang ditentukan) dan sekali lagi implementasinya benar.Equals
karenaIEqualityComparer<T>
antarmuka. Yang harus Anda lihat adalah nama pembanding itu sendiri . Dalam hal iniMultiSetComparer
yang masuk akal.Solusi sederhana dan cukup efisien adalah menyortir kedua koleksi dan kemudian membandingkannya untuk kesetaraan:
Algoritma ini adalah O (N * logN), sedangkan solusi Anda di atas adalah O (N ^ 2).
Jika koleksi memiliki sifat-sifat tertentu, Anda mungkin dapat menerapkan solusi yang lebih cepat. Misalnya, jika kedua koleksi Anda adalah kumpulan hash, mereka tidak dapat berisi duplikat. Juga, memeriksa apakah hash set mengandung beberapa elemen sangat cepat. Dalam hal ini algoritma yang mirip dengan Anda kemungkinan akan menjadi yang tercepat.
sumber
Buat Kamus "dict" dan kemudian untuk setiap anggota dalam koleksi pertama, lakukan dict [anggota] ++;
Kemudian, lingkarkan koleksi kedua dengan cara yang sama, tetapi untuk setiap anggota lakukan dikt [anggota] -.
Pada akhirnya, lingkar semua anggota dalam kamus:
Sunting: Sejauh yang saya tahu ini berada pada urutan yang sama dengan algoritma yang paling efisien. Algoritma ini adalah O (N), dengan asumsi bahwa Kamus menggunakan pencarian O (1).
sumber
return dict.All(kvp => kvp.Value == 0);
Ini adalah implementasi generik saya (sangat dipengaruhi oleh D.Jennings) dari metode perbandingan (dalam C #):
sumber
The keys of a dictionary are compared by reference, so we have to find the original key that is equivalent to the "item"
- ini tidak benar. Algoritma ini didasarkan pada asumsi yang salah dan sementara bekerja, itu sangat tidak efisien.Anda bisa menggunakan Hashset . Lihatlah metode SetEquals .
sumber
Jika Anda menggunakan Shouldly , Anda bisa menggunakan ShouldAllBe with Contains.
Dan akhirnya, Anda dapat menulis ekstensi.
MEMPERBARUI
Parameter opsional ada pada metode ShouldBe .
sumber
bool ignoreOrder
pada metode ShouldBe .EDIT: Saya menyadari segera setelah saya berpose bahwa ini benar-benar hanya berfungsi untuk set - itu tidak akan berurusan dengan koleksi yang memiliki item duplikat. Misalnya {1, 1, 2} dan {2, 2, 1} akan dianggap sama dari perspektif algoritma ini. Namun, jika koleksi Anda ditetapkan (atau kesetaraannya dapat diukur dengan cara itu), saya harap Anda menemukan di bawah ini berguna.
Solusi yang saya gunakan adalah:
Linq melakukan hal kamus di bawah selimut, jadi ini juga O (N). (Catatan, ini O (1) jika koleksi tidak berukuran sama).
Saya melakukan pemeriksaan kewarasan menggunakan metode "SetEqual" yang disarankan oleh Daniel, metode OrderBy / SequenceEquals yang disarankan oleh Igor, dan saran saya. Hasilnya di bawah ini, menunjukkan O (N * LogN) untuk Igor dan O (N) untuk saya dan Daniel.
Saya pikir kesederhanaan dari kode interseksi Linq menjadikannya solusi yang lebih disukai.
sumber
Jika tidak ada pengulangan dan tanpa urutan, EqualityComparer berikut dapat digunakan untuk memungkinkan koleksi sebagai kunci kamus:
Berikut ini adalah implementasi ToHashSet () yang saya gunakan. The algoritma kode hash berasal dari Jawa Efektif (dengan cara Jon Skeet).
sumber
ISet<T>
untuk mengekspresikannya dimaksudkan untuk set (yaitu tidak ada duplikat).ISet
, ide di sini adalah untuk memperlakukanIEnumerable
sebagai satu set (karena Anda harusIEnumerable
memulai dengan), meskipun mempertimbangkan 0 upvotes di lebih 5 tahun yang mungkin bukan ide yang paling tajam: PSolusi membutuhkan .NET 3.5 dan
System.Collections.Generic
namespace. Menurut Microsoft ,SymmetricExceptWith
adalah operasi O (n + m) , dengan n mewakili jumlah elemen di set pertama dan m mewakili jumlah elemen di set kedua. Anda selalu bisa menambahkan pembanding kesetaraan ke fungsi ini jika perlu.sumber
Mengapa tidak digunakan. Kecuali ()
http://msdn.microsoft.com/en-us/library/bb397894.aspx
sumber
Except
tidak akan berfungsi untuk menghitung item duplikat. Ini akan mengembalikan true untuk set {1,2,2} dan {1,1,2}.[1, 1, 2] != [1, 2, 2]
. MenggunakannyaDistinct
akan membuat mereka terlihat sama.Posting rangkap jenis, tetapi periksa solusi saya untuk membandingkan koleksi . Sederhana saja:
Ini akan melakukan perbandingan kesetaraan terlepas dari pesanan:
Ini akan memeriksa untuk melihat apakah item ditambahkan / dihapus:
Ini akan melihat item apa dalam kamus berubah:
Posting asli di sini .
sumber
erickson hampir benar: karena Anda ingin mencocokkan jumlah duplikat, Anda menginginkan sebuah Tas . Di Jawa, ini terlihat seperti:
Saya yakin C # memiliki implementasi Set bawaan. Saya akan menggunakannya dulu; jika kinerja merupakan masalah, Anda selalu dapat menggunakan implementasi Set yang berbeda, tetapi menggunakan antarmuka Set yang sama.
sumber
Inilah varian metode ekstensi dari jawaban ohadsc, seandainya bermanfaat bagi seseorang
sumber
IEnumerable<T>
ada pertanyaan, maka meneleponCount()
bukanlah ide yang baik. Pendekatan jawaban asli Ohad untuk memeriksa apakah merekaICollection<T>
adalah ide yang lebih baik.Berikut adalah solusi yang merupakan perbaikan dari yang ini .
sumber
Ada banyak solusi untuk masalah ini. Jika Anda tidak peduli dengan duplikat, Anda tidak perlu mengurutkan keduanya. Pertama pastikan bahwa mereka memiliki jumlah item yang sama. Setelah itu mengurutkan salah satu koleksi. Kemudian, binsearch setiap item dari koleksi kedua di koleksi yang diurutkan. Jika Anda tidak menemukan item yang diberikan berhenti dan kembali salah. Kompleksitasnya: - mengurutkan koleksi pertama: N Log (N) - mencari setiap item dari yang kedua menjadi yang pertama: NLOG (N) sehingga Anda mendapatkan 2 * N * LOG (N) dengan asumsi mereka cocok dan Anda mencari semuanya. Ini mirip dengan kompleksitas penyortiran keduanya. Ini juga memberi Anda manfaat untuk berhenti lebih awal jika ada perbedaan. Namun, perlu diingat bahwa jika keduanya diurutkan sebelum Anda masuk ke perbandingan ini dan Anda mencoba mengurutkan dengan menggunakan sesuatu seperti qsort, pengurutan akan lebih mahal. Ada optimisasi untuk ini. Alternatif lain, yang sangat bagus untuk koleksi kecil di mana Anda tahu kisaran elemen adalah dengan menggunakan indeks bitmask. Ini akan memberi Anda O (n) kinerja. Alternatif lain adalah menggunakan hash dan mencarinya. Untuk koleksi kecil biasanya jauh lebih baik untuk melakukan pengurutan atau indeks bitmask. Hashtable memiliki kelemahan lokalitas yang lebih buruk jadi ingatlah itu. Sekali lagi, itu hanya jika Anda tidak t peduli duplikat. Jika Anda ingin menghitung duplikat, lanjutkan dengan menyortir keduanya.
sumber
Dalam banyak kasus satu-satunya jawaban yang cocok adalah salah satu dari Igor Ostrovsky, jawaban lain didasarkan pada kode hash objek. Tetapi ketika Anda menghasilkan kode hash untuk objek Anda melakukannya hanya berdasarkan bidang IMMUTABLE-nya - seperti bidang Id objek (dalam kasus entitas database) - Mengapa penting untuk mengganti GetHashCode ketika metode Equals ditimpa?
Ini berarti, bahwa jika Anda membandingkan dua koleksi, hasilnya mungkin benar dari metode perbandingan meskipun bidang item yang berbeda tidak sama. Untuk membandingkan jauh koleksi, Anda harus menggunakan metode Igor dan mengimplementasikan IEqualirity.
Silakan baca komentar saya dan mr.Schnider di pos yang paling banyak dipilihnya.
James
sumber
Mengizinkan duplikat di
IEnumerable<T>
(jika set tidak diinginkan \ mungkin) dan "mengabaikan pesanan" Anda harus dapat menggunakan a.GroupBy()
.Saya bukan ahli dalam pengukuran kompleksitas, tetapi pemahaman dasar saya adalah bahwa ini harus O (n). Saya mengerti O (n ^ 2) berasal dari melakukan operasi O (n) di dalam operasi O (n) lainnya seperti
ListA.Where(a => ListB.Contains(a)).ToList()
. Setiap item di ListB dievaluasi untuk kesetaraan terhadap setiap item di ListA.Seperti yang saya katakan, pemahaman saya tentang kompleksitas terbatas, jadi perbaiki saya jika saya salah.
sumber
Solusi sederhana ini memaksa
IEnumerable
tipe generik untuk diimplementasikanIComparable
. KarenaOrderBy
definisi.Jika Anda tidak ingin membuat asumsi seperti itu tetapi masih ingin menggunakan solusi ini, Anda dapat menggunakan potongan kode berikut:
sumber
Jika membandingkan untuk tujuan Asertions Pengujian Unit, mungkin masuk akal untuk membuang beberapa efisiensi keluar jendela dan cukup mengkonversi setiap daftar ke representasi string (csv) sebelum melakukan perbandingan. Dengan begitu, pesan Pernyataan pengujian standar akan menampilkan perbedaan dalam pesan kesalahan.
Pemakaian:
Metode Ekstensi Pembantu:
sumber