Apa. Koleksi NET menyediakan pencarian tercepat

143

Saya memiliki item 60k yang perlu diperiksa dengan daftar pencarian 20k. Apakah ada objek koleksi (seperti List, HashTable) yang menyediakan metode yang sangat cepat Contains()? Atau apakah saya harus menulis sendiri? Di kata lain, adalah Contains()metode default hanya memindai setiap item atau apakah itu menggunakan algoritma pencarian yang lebih baik.

foreach (Record item in LargeCollection)
{
    if (LookupCollection.Contains(item.Key))
    {
       // Do something
    }
}

Catatan . Daftar pencarian sudah diurutkan.

Ondrej Janacek
sumber
Contains for List tidak berfungsi untuk daftar objek karena membandingkan referensi.
Fiur
2
Mengurutkan data? Pencarian biner - lihat jawaban @ Mark.
Hamish Smith
HashtTable mengalahkan apa pun hingga 2 juta item dalam pengalaman saya
Chris S
Selain itu, jika elemen Anda berada dalam urutan yang berarti dan didistribusikan secara merata, Anda dapat melakukan pencarian biner jauh lebih cepat dengan membuat tebakan pertama Anda berada dalam kisaran perkiraan item Anda. Ini mungkin atau mungkin tidak memiliki arti untuk aplikasi spesifik Anda.
Brian
2
Jangan lupa tentang System.Collections.Generic.SortedList (TKey, TValue) jika Anda ingin menyederhanakan hal ini tetapi hindari hashset.
Brian

Jawaban:

141

Dalam kasus yang paling umum, pertimbangkan System.Collections.Generic.HashSetsebagai struktur data kuda kerja "Berisi" default Anda, karena memerlukan waktu yang konstan untuk mengevaluasi Contains.

Jawaban aktual untuk "Apa koleksi tercepat yang bisa dicari" tergantung pada ukuran data spesifik Anda, keteraturan, biaya, dan frekuensi pencarian.

Jimmy
sumber
36
Catatan: Jangan lupa untuk mengganti fungsi kode hash. Untuk kinerja tambahan, pregenerate kode hash Anda di konstruktor Anda.
Brian
1
@ Brian: poin bagus. Saya mengasumsikan (tanpa dasar) Record. Key adalah tipe builtin dari beberapa jenis.
Jimmy
3
@Brian: daripada pregenerating saya lebih suka menyimpan yang dihasilkan pertama kali, mengapa harus memperlambat konstruktor dengan sesuatu yang Anda tidak tahu apakah itu akan digunakan?
jmservera
8
FYI: Tes kinerja - Saya membuat perbandingan antara Daftar <T> dan HashSet <T> untuk string. Saya menemukan bahwa HashSet sekitar 1000 kali lebih cepat daripada Daftar.
Quango
10
@Quango: 3 tahun kemudian, tetapi sungguh jika Anda tidak menentukan ukuran data Anda, mengatur perbandingan kinerja ini tidak berarti apa-apa: Hashsets memiliki pencarian O (1), daftar memiliki pencarian O (n), sehingga rasio kinerja sebanding dengan n.
Clément
73

Jika Anda tidak perlu memesan, coba HashSet<Record>(baru ke .Net 3.5)

Jika ya, gunakan a List<Record>dan panggil BinarySearch.

Slaks
sumber
8
Atau, dalam .NET> = 4, gunakan SortedSet
StriplingWarrior
2
Atau lebih baik lagi, ImmutableSortedSetdari System.ImmutableCollections
Alexei S
24

Sudahkah Anda mempertimbangkan List.BinarySearch(item)?

Anda mengatakan bahwa koleksi besar Anda sudah diurutkan jadi ini sepertinya peluang yang sempurna? Hash pasti akan menjadi yang tercepat, tetapi ini membawa masalah sendiri dan membutuhkan lebih banyak overhead untuk penyimpanan.

Menandai
sumber
1
Anda benar, hash dapat membawa beberapa masalah yang tidak diinginkan saat menggunakan objek yang bisa berubah sebagai kunci.
jmservera
10

Anda harus membaca blog ini yang menguji beberapa jenis koleksi dan metode untuk masing-masing menggunakan teknik tunggal dan multi-utas.

Menurut hasil, BinarySearch pada Daftar dan SortedList adalah berkinerja terbaik yang terus-menerus berjalan leher ketika mencari sesuatu sebagai "nilai".

Saat menggunakan koleksi yang memungkinkan untuk "kunci", Kamus, ConcurrentDictionary, Hashset, dan HashTables melakukan yang terbaik secara keseluruhan.


sumber
4

Simpan kedua daftar x dan y dalam urutan yang diurutkan.

Jika x = y, lakukan tindakan Anda, jika x <y, lanjut x, jika y <x, lanjutkan y hingga daftar kosong.

Run time dari persimpangan ini sebanding dengan min (ukuran (x), ukuran (y))

Jangan menjalankan loop .Contains (), ini sebanding dengan x * y yang jauh lebih buruk.

clemahieu
sumber
+1 untuk algoritma yang lebih efisien. Bahkan jika daftar saat ini tidak disortir, akan lebih efisien untuk mengurutkannya terlebih dahulu dan kemudian menjalankan algoritma ini.
Matt Boehm
Bukankah runtime sebanding dengan max (ukuran (x), ukuran (y)) dalam skenario terburuk? Contoh: int [] x = {99.100}; int [] y = {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
Matt Boehm
Tidak karena setelah Anda menyelesaikan set yang lebih kecil, Anda dapat menambahkan elemen yang tersisa dari set yang lebih besar karena mereka sudah diurutkan. Saya pikir proses ini mirip dengan Gabung Sortir.
3

Jika mungkin untuk mengurutkan item Anda maka ada cara yang jauh lebih cepat untuk melakukan ini kemudian melakukan pencarian kunci ke dalam hashtable atau b-tree. Meskipun jika item Anda tidak dapat diurutkan, Anda tidak bisa memasukkannya ke dalam b-tree.

Ngomong-ngomong, jika sortir urutkan kedua daftar maka tinggal mengurutkan daftar pencarian secara berurutan.

Walk lookup list
   While items in check list <= lookup list item
     if check list item = lookup list item do something
   Move to next lookup list item
Schuler yang kaya
sumber
Ya benar sekali. Jika Anda memiliki dua daftar yang disortir, Anda hanya perlu melintasi satu kali.
denver
3

Jika Anda menggunakan .Net 3.5, Anda dapat membuat kode yang lebih bersih menggunakan:

foreach (Record item in LookupCollection.Intersect(LargeCollection))
{
  //dostuff
}

Saya tidak punya. Net 3.5 di sini dan ini belum teruji. Itu bergantung pada metode ekstensi. Bukan itu LookupCollection.Intersect(LargeCollection)mungkin tidak sama dengan LargeCollection.Intersect(LookupCollection)... yang terakhir mungkin jauh lebih lambat.

Ini mengasumsikan LookupCollection adalah a HashSet

Brian
sumber
2

Jika Anda tidak khawatir tentang mencicit setiap bit kinerja, saran untuk menggunakan HashSet atau pencarian biner adalah solid. Kumpulan data Anda tidak cukup besar sehingga ini akan menjadi masalah 99% dari waktu.

Tetapi jika ini hanya satu dari ribuan kali Anda akan melakukan ini dan kinerjanya sangat penting (dan terbukti tidak dapat diterima menggunakan HashSet / pencarian biner), Anda tentu bisa menulis algoritma Anda sendiri yang berjalan di daftar yang diurutkan melakukan perbandingan saat Anda pergi. Setiap daftar akan berjalan paling banyak sekali dan dalam kasus-kasus patologis tidak akan buruk (begitu Anda pergi rute ini Anda mungkin akan menemukan bahwa perbandingan, dengan asumsi itu adalah string atau nilai non-integral lainnya, akan menjadi biaya riil dan mengoptimalkan itu akan menjadi langkah berikutnya).

Robert Horvick
sumber