Hangi .NET koleksiyonu en hızlı arama sağlar


143

20k arama listesine göre kontrol edilmesi gereken 60k öğem var. İstisnai olarak hızlı bir yöntem sağlayan bir toplama nesnesi (gibi List, HashTable) var Contains()mı? Yoksa kendim yazmak zorunda mıyım? Diğer bir deyişle, varsayılan Contains()yöntem her öğeyi taramak mı yoksa daha iyi bir arama algoritması mı kullanmaktır.

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

Not . Arama listesi zaten sıralanmış.


Liste İçerir, referansları karşılaştırdığı için nesne listesi için çalışmaz.
Fiur

2
Veriler sıralandı mı? İkili arama - @ Mark'ın cevabına bakınız.
Hamish Smith

HashtTable, deneyimimde 2 metreye kadar bir şey atıyor
Chris S

Bir kenara, öğeleriniz anlamlı bir düzendeyse ve oldukça eşit bir şekilde dağıtılmışsa, ilk tahminlerinizin öğenizin tahmini aralığı içinde olmasını sağlayarak ikili bir aramayı çok daha hızlı yapabilirsiniz. Bunun özel uygulamanız için bir anlamı olabilir veya olmayabilir.
Brian

2
Bu şeyleri basitleştirmek ama bir karmaşayı önlemek istiyorsanız System.Collections.Generic.SortedList (TKey, TValue) 'u unutmayın.
Brian

Yanıtlar:


141

En genel durumda, System.Collections.Generic.HashSetvarsayılan "İçerir" işgücü veri yapınız olarak düşünün , çünkü değerlendirmek sürekli zaman alırContains .

"En hızlı aranabilir koleksiyon nedir" sorusunun gerçek yanıtı, veri boyutunuza, sıralılık, karma maliyet ve arama sıklığına bağlıdır.


36
Not: Hashcode işlevini geçersiz kılmayı unutmayın. Daha fazla performans için yapıcıda karma kodunuzu önceden oluşturun.
Brian

1
@Brian: iyi bir nokta. Record (temelde) Record.Key yerleşik bir tür olduğunu varsayıyordum.
Jimmy

3
@Brian: Önceden oluşturmak yerine, üretileni ilk kez saklamayı tercih ediyorum, neden kullanılıp kullanılmayacağını bilmediğiniz bir şeyle yapıcıyı yavaşlatmak?
jmservera

8
FYI: Performans testi - Dizeler için List <T> ve HashSet <T> arasında bir karşılaştırma oluşturdum. HashSet'in List'ten yaklaşık 1000 kat daha hızlı olduğunu gördüm.
Quango

10
@Quango: 3 yıl sonra, ancak gerçekten veri kümenizin boyutunu belirtmezseniz, bu performans karşılaştırması hiçbir şey ifade etmez: Hashsets O (1) aramasına, listelerin O (n) aramasına sahiptir, bu nedenle performans oranı orantılıdır. n.
Clément

73

Siparişe ihtiyacınız yoksa, deneyin HashSet<Record>(.Net 3.5'te yeni)

Bunu yaparsanız, bir List<Record>ve kullanın BinarySearch.


8
Veya .NET> = 4'te, SortedSet
StriplingWarrior

2
Veya daha iyisi, ImmutableSortedSetSystem.ImmutableCollections
Alexei S

24

Düşündün mü List.BinarySearch(item) ?

Büyük koleksiyonunuzun zaten sıralandığını söylediniz, bu mükemmel bir fırsat gibi görünüyor mu? Bir karma kesinlikle en hızlı olurdu, ancak bu kendi sorunlarını beraberinde getirir ve depolama için çok daha fazla ek yük gerektirir.


1
Haklısın, bir karma değişebilir nesneleri anahtar olarak kullanırken bazı istenmeyen sorunlar getirebilir.
jmservera

10

Hem tek hem de çok iş parçacıklı teknikler kullanarak her biri için birkaç farklı koleksiyon türünü ve yöntemi hız test eden bu blogu okumalısınız .

Sonuçlara göre, bir Listedeki bir BinarySearch ve SortedList, bir "değer" olarak bakarken sürekli olarak boynundan koşan en iyi performans gösterenlerdi.

"Anahtar" lara izin veren bir koleksiyon kullanırken, Dictionary, ConcurrentDictionary, Hashset ve HashTables genel olarak en iyi performansı gösterir.


4

Her iki x ve y listesini sıralı olarak tutun.

X = y ise, eyleminizi yapın, x <y ise, x ilerleyin, y <x ise, her iki liste de boş olana kadar y ilerleyin.

Bu kesişimin çalışma süresi min (boyut (x), boyut (y)) ile orantılıdır.

Do bir .Contains () döngü çalıştırın bu daha kötü x * y orantılıdır.


Daha verimli algoritma için +1. Listeler şu anda ayrılmamış olsa bile, önce bunları sıralayıp daha sonra bu algoritmayı çalıştırmak daha verimli olacaktır.
Matt Boehm

En kötü senaryoda çalışma zamanı max (boyut (x), boyut (y)) ile orantılı olmaz mı? Örnek: int [] x = {99,100}; int [] y = {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
Matt Boehm

Hayır, çünkü daha küçük kümeyi tamamladıktan sonra, zaten sıralandıkları için kalan öğeleri daha büyük kümeden ekleyebilirsiniz. Bence bu süreç Merge Sort ile benzer.

3

Öğelerinizi sıralamak mümkünse, bunu yapmanın çok daha hızlı bir yolu vardır, ardından anahtar aramaları hashtable veya b ağacına dönüştürür. Öğeleriniz sıralanabilir değilse de, onları gerçekten bir b ağacına koyamazsınız.

Her neyse, sıralanabilir her iki listeyi sıralarsanız, bu sadece arama listesini sırayla yürümek meselesidir.

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

Evet çok doğru. İki sıralı listeniz varsa, yalnızca bir kez geçiş yapmanız gerekir.
denver

3

Net 3.5 kullanıyorsanız, aşağıdakileri kullanarak daha temiz kodlar yapabilirsiniz:

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

Burada .Net 3.5 yok ve bu yüzden test edilmemiştir. Bir uzatma yöntemine dayanır. Değil o LookupCollection.Intersect(LargeCollection)muhtemelen aynı değildir LargeCollection.Intersect(LookupCollection)ikincisi çok daha yavaş muhtemelen ....

Bu, LookupCollection öğesinin HashSet


2

Performansın her bir son bitini gıcırdatmaktan endişe etmiyorsanız, HashSet veya ikili arama kullanma önerisi sağlamdır. Veri kümeleriniz bunun% 99'luk bir sorun olacağı kadar büyük değil.

Ancak, bunu binlerce kez gerçekleştirirseniz ve performans kritikse (ve HashSet / ikili arama kullanarak kabul edilemez olduğu kanıtlanmışsa), gittikçe karşılaştırmalar yaparak sıralama listelerini yürüten kendi algoritmanızı yazabilirsiniz. Her liste en fazla bir kez yürüdü ve patolojik durumlarda kötü olmazdı (bu yola girdikten sonra muhtemelen bir dize veya diğer ayrılmaz bir değer olduğu varsayılarak karşılaştırmanın gerçek gider olacağını ve bu optimizasyonun bir sonraki adım olacağını unutmayın).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.