最速の検索を提供する.NETコレクション


143

20,000のルックアップリストに対してチェックする必要がある60kのアイテムがあります。(のようなコレクションオブジェクトが存在しListHashTableexceptionly速い提供)Contains()方法は?それとも自分で書く必要がありますか?つまり、デフォルトのContains()方法は、各項目をスキャンするだけですか、それともより優れた検索アルゴリズムを使用しますか。

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

。ルックアップリストは既に並べ替えられています。


それが参照を比較しているため、リストのコンテナはオブジェクトのリストに対しては機能しません。
フィア2009年

2
ソートされたデータ?バイナリ検索-@Markの回答を参照してください。
Hamish Smith

HashtTableは私の経験で200万アイテムまで何でも勝ちます
Chris S

余談ですが、要素が意味のある順序であり、かなり均等に分散されている場合、最初の推測をアイテムの推定範囲内にすることで、バイナリ検索をはるかに高速に行うことができます。これは、特定のアプリケーションにとって意味がある場合とない場合があります。
ブライアン

2
これを単純化してハッシュセットを避けたい場合は、System.Collections.Generic.SortedList(TKey、TValue)を忘れないでください。
ブライアン

回答:


141

最も一般的なケースでは、System.Collections.Generic.HashSet評価に一定の時間がかかるため、デフォルトの「含む」の主要なデータ構造を検討してくださいContains

「最速の検索可能なコレクションとは」に対する実際の答えは、特定のデータサイズ、順序付け、ハッシュのコスト、および検索頻度によって異なります。


36
注:ハッシュコード関数をオーバーライドすることを忘れないでください。パフォーマンスを向上させるには、コンストラクターでハッシュコードを事前生成します。
ブライアン

1
@ブライアン:良い点。Recordbaseは(ベースレスで)Record.Keyが何らかの組み込み型であると想定していました。
ジミー、

3
@ブライアン:事前に生成するのではなく、生成されたものを最初に保存するほうが好きですが、コンストラクターを使用するかどうかわからないものでコンストラクターを遅くするのはなぜですか?
jmservera 2009年

8
参考:パフォーマンステスト-文字列のList <T>とHashSet <T>の比較を作成しました。HashSetはListよりも約1000倍高速であることがわかりました。
Quango

10
@Quango:3年後ですが、実際にデータセットのサイズを指定しない場合、このパフォーマンスの比較は意味がありません。ハッシュセットにはO(1)検索があり、リストにはO(n)検索があるため、パフォーマンス比はn。
クレメント

73

注文する必要がない場合は、HashSet<Record>(。Net 3.5の新機能)を試してください。

その場合は、を使用してList<Record>を呼び出しますBinarySearch


8
または、.NET> = 4では、SortedSet
StriplingWarrior

2
またはImmutableSortedSet、System.ImmutableCollectionsから-Alexei
S

24

検討しましたList.BinarySearch(item)か?

大規模なコレクションは既に並べ替えられているので、これは絶好の機会のようですか?ハッシュは間違いなく最速ですが、これはそれ自体の問題を引き起こし、ストレージに多くのオーバーヘッドを必要とします。


1
そうです、可変オブジェクトをキーとして使用すると、ハッシュが望ましくない問題を引き起こす可能性があります。
jmservera 2009年

10

シングルとマルチスレッドの両方の手法を使用して、いくつかの異なるタイプのコレクションとメソッドをスピードテストしたこのブログを読む必要があります。

結果によると、リストのBinarySearchとSortedListは、何かを「値」として検索するときに首を絞って実行しているトップパフォーマーでした。

「キー」を許可するコレクションを使用すると、Dictionary、ConcurrentDictionary、Hashset、およびHashTablesが全体的に最高のパフォーマンスを発揮しました。


4

xとyの両方のリストをソートされた順序で保持します。

x = yの場合はアクションを実行し、x <yの場合はxを進め、y <xの場合はいずれかのリストが空になるまでyを進めます。

この交差の実行時間は、最小値(サイズ(x)、​​サイズ(y))に比例します。

.Contains()ループを実行しないでください。これはx * yに比例しますが、はるかに悪いです。


より効率的なアルゴリズムの+1。リストが現在ソートされていない場合でも、最初にリストをソートしてからこのアルゴリズムを実行する方が効率的です。
マットベーム

しかし、最悪の場合のシナリオでは、ランタイムはmax(size(x)、size(y))に比例しませんか?例:int [] x = {99,100}; int [] y = {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
マットベーム

いいえ、小さいセットを完了すると、それらはすでにソートされているため、大きいセットの残りの要素を追加できます。このプロセスはマージソートに似ていると思います。

3

アイテムを並べ替えることができる場合は、これを行うより高速な方法があり、ハッシュテーブルまたはBツリーへのキールックアップを実行します。アイテムが並べ替えできない場合は、とにかく実際にそれらをBツリーに入れることはできません。

とにかく、両方のリストをソート可能な場合は、ルックアップリストを順番に歩くだけです。

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

はい、そうです。ソートされたリストが2つある場合は、それぞれを1回だけトラバースする必要があります。
デンバー

3

.Net 3.5を使用している場合は、次を使用してよりクリーンなコードを作成できます。

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

ここには.Net 3.5がないので、これはテストされていません。それは拡張メソッドに依存しています。それLookupCollection.Intersect(LargeCollection)はおそらく同じではありませんLargeCollection.Intersect(LookupCollection)...後者はおそらくはるかに遅いです。

これは、LookupCollectionが HashSet


2

パフォーマンスの最後のすべてのビットをきしむことについて心配していなければ、HashSetまたはバイナリ検索を使用するという提案は確実です。データセットが十分に大きくないため、99%の確率でこれが問題になります。

しかし、これが数千回の1回だけであり、パフォーマンスが重要である場合(そしてHashSet /バイナリ検索を使用して受け入れられないことが判明している場合)、比較しながらソートリストをウォークする独自のアルゴリズムを書くことができます。各リストは最大で1回だけ実行され、病理学的なケースでは悪くはありません(このルートをたどると、文字列または他の非整数値であると仮定して、比較が実際の費用となり、それを最適化することが次のステップです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.