web-dev-qa-db-ja.com

HashSet <T>タイプはいつ使用する必要がありますか?

私はHashSet<T>タイプを調査していますが、コレクション内の位置がわかりません。

List<T>を置き換えるために使用できますか? HashSet<T>のパフォーマンスは向上すると思いますが、その要素への個々のアクセスを確認できませんでした。

列挙のみですか?

127
Joan Venge

HashSet<T>についての重要なことは、名前の中にあります。それはsetです。単一のセットでできることは、そのメンバーが何であるかを確立し、アイテムがメンバーであるかどうかを確認することだけです。

単一の要素(例:set[45])を取得できるかどうかを尋ねることは、セットの概念を誤解しています。セットの45番目の要素のようなものはありません。セット内のアイテムには順序がありません。セット{1、2、3}と{2、3、1}は、メンバーシップが同じであるため、あらゆる点で同一であり、メンバーシップが重要です。

HashSet<T>を反復処理すると、セット内の項目に順序が課されるため、やや危険です。その順序は、実際にはセットのプロパティではありません。あなたはそれに頼るべきではありません。コレクション内のアイテムの順序が重要な場合、そのコレクションはセットではありません。

セットは本当に限られており、ユニークなメンバーがいます。一方、彼らは本当に速いです。

224
Robert Rossney

HashSet<string>を使用する実際の例を次に示します。

UnrealScriptファイルの構文ハイライターの一部は、 Doxygenスタイルのコメントを強調表示する という新機能です。 @または\コマンドが有効かどうかを判断して、グレー(有効)または赤(無効)で表示するかどうかを判断できる必要があります。すべての有効なコマンドのHashSet<string>があるので、レクサーで@xxxトークンをヒットするたびに、O(1)妥当性チェックとしてvalidCommands.Contains(tokenText)を使用します。有効なコマンドのset内のコマンドのexistence以外は何も気にしません。私が直面した代替案を見てみましょう:

  • Dictionary<string, ?>:値にはどのタイプを使用しますか? ContainsKeyを使用するだけなので、値は無意味です。注:.NET 3.0以前は、これがO(1)ルックアップの唯一の選択肢でした-HashSet<T>は3.0に追加され、4.0のISet<T>を実装するために拡張されました。
  • List<string>:リストをソートしたままにすると、BinarySearchを使用できます。これはO(log n)です(上記のこの事実は見ませんでした)。ただし、有効なコマンドのリストは変更されない固定リストであるため、これは単純なものよりも適切ではありません...
  • string[]:繰り返しますが、Array.BinarySearchはO(log n)パフォーマンスを提供します。リストが短い場合、これは最高のパフォーマンスのオプションになる可能性があります。 HashSetDictionary、またはListよりも常にスペースのオーバーヘッドが小さくなります。 BinarySearchを使用しても、大きなセットの場合は高速ではありませんが、小さなセットの場合は実験する価値があります。鉱山には数百のアイテムがあるので、私はこれを渡しました。
104
Sam Harwell

HashSet<T>ICollection<T>インターフェース:

public interface ICollection<T> : IEnumerable<T>, IEnumerable
{
    // Methods
    void Add(T item);
    void Clear();
    bool Contains(T item);
    void CopyTo(T[] array, int arrayIndex);
    bool Remove(T item);

    // Properties
   int Count { get; }
   bool IsReadOnly { get; }
}

List<T>実装IList<T>、これはICollection<T>

public interface IList<T> : ICollection<T>
{
    // Methods
    int IndexOf(T item);
    void Insert(int index, T item);
    void RemoveAt(int index);

    // Properties
    T this[int index] { get; set; }
}

HashSetにはセマンティクスが設定されており、内部的にハッシュテーブルを介して実装されています。

セットは、重複する要素を含まないコレクションであり、要素の順序は特にありません。

HashSetがインデックス/位置/リストの動作を失った場合、何が得られますか?

HashSetからのアイテムの追加と取得は、インデクサー経由ではなく、常にオブジェクト自体によって行われ、O(1)操作(List is O(1)追加、O(1)インデックスによる取得、O(n)検索/削除)。

HashSetの動作は、Dictionary<TKey,TValue>キーを値として追加/削除するだけで、辞書の値自体を無視します。ディクショナリ内のキーには重複した値がないことが期待されますが、それが「セット」部分のポイントです。

23
Kenan E. K.

リストよりもハッシュセットを選択するのは、パフォーマンスが悪い理由です。代わりに、あなたの意図をより良く捉えるものは何ですか?順序が重要な場合は、Set(またはHashSet)が出ています。重複が許可されている場合も同様です。しかし、順序を気にしない場合は多くの状況があり、重複したくない場合があります-それがセットを必要とする場合です。

14
Carl Manaster

HashSetは、ハッシュによって実装されるsetです。セットは、重複する要素を含まない値のコレクションです。セット内の値も通常、順序付けされていません。そのため、リストを置き換えるためにセットを使用することはできません(最初にセットを使用する必要がある場合を除きます)。

セットが何のために良いのか疑問に思っているなら:明らかに、重複を取り除きたい場所。少し不自然な例として、ソフトウェアプロジェクトの10.000リビジョンのリストがあり、そのプロジェクトに貢献した人の数を知りたいとします。 Set<string>を使用して、リビジョンのリストを反復処理し、各リビジョンの作成者をセットに追加できます。繰り返しが完了したら、セットのサイズが探していた答えになります。

11
earl

HashSetは、IEnumerbleコレクション内の重複する要素を削除するために使用されます。例えば、

List<string> duplicatedEnumrableStrings = new List<string> {"abc", "ghjr", "abc", "abc", "yre", "obm", "ghir", "qwrt", "abc", "vyeu"};
HashSet<string> uniqueStrings = new HashSet(duplicatedEnumrableStrings);

それらのコードが実行された後、uniqueStringsは{"abc"、 "ghjr"、 "yre"、 "obm"、 "qwrt"、 "vyeu"}を保持します。

8
Thomas.Benz

おそらく、ハッシュセットの最も一般的な使用法は、特定の要素が含まれているかどうかを確認することです。これは、O(1)十分に強力なハッシュ関数を想定)包含チェックがO(n)(およびO(log n)であるソートされたセット)であるリスト。したがって、多くのチェックを行う場合、アイテムが含まれているかどうかいくつかのリスト、hahssetsはパフォーマンスの向上につながる可能性があります。それらを繰り返しただけでも、大きな違いはありません(リスト全体と同じです。 )。

いいえ、セットにインデックスを付けることはできません。セットは順序付けされていないため、とにかく意味がありません。いくつかの項目を追加すると、セットは最初の項目と2番目の項目などを記憶しません。

6
sepp2k

_HashSet<T>_は、 数学セット をオブジェクトとして表すことができる.NETフレームワークのデータ構造です。この場合、ハッシュコード(各アイテムのGetHashCode結果)を使用して、セット要素の等価性を比較します。

セットは、その中に含まれる同じ要素の1つのオカレンスのみを許可するという点でリストと異なります。 _HashSet<T>_は、2番目の同一の要素を追加しようとすると、単にfalseを返します。実際、内部データ構造は単なるハッシュテーブルであるため、要素の検索は非常に高速です(O(1) time)。

どちらを使用するか迷っている場合、_List<T>_が_HashSet<T>_が適切な場所で使用することは最大の間違いではないことに注意してください。さらに、ルックアップ(アイテムの取得)は非常に効率的です-理想的にはO(1) timeではなくO(n)(完全なバケティング用)-多くのシナリオで非常に重要です。

4
Noldorin

List<T>は、情報の順序付きセットを格納するために使用されます。リストの要素の相対的な順序がわかっている場合は、一定の時間でそれらにアクセスできます。ただし、要素がリスト内のどこにあるかを判別したり、リスト内に要素が存在するかどうかを確認したりするために、ルックアップ時間は線形です。一方、HashedSet<T>は保存されたデータの順序を保証せず、その結果、その要素に一定のアクセス時間を提供します。

名前が示すとおり、HashedSet<T>は、 セマンティクスの設定 を実装するデータ構造です。データ構造は、集合演算(つまり、Union、Difference、Intersect)を実装するために最適化されています。これは、従来のList実装では効率的に実行できません。

したがって、使用するデータ型を選択するかどうかは、実際にアプリケーションで何をしようとしているかによって異なります。コレクション内での要素の順序を気にせず、列挙または存在の確認のみを行う場合は、HashSet<T>を使用します。それ以外の場合は、List<T>または別の適切なデータ構造の使用を検討してください。

4
Steve Guidi

基本的なシナリオでは、LINQが提供するよりも2つのコレクションに対してより具体的な集合演算が必要な場合は、HashSet<T>を使用する必要があります。 DistinctUnionIntersectExceptなどのLINQメソッドはほとんどの状況で十分ですが、場合によってはよりきめ細かい操作が必要になることがあり、HashSet<T>は以下を提供します:

  • UnionWith
  • IntersectWith
  • ExceptWith
  • SymmetricExceptWith
  • Overlaps
  • IsSubsetOf
  • IsProperSubsetOf
  • IsSupersetOf
  • IsProperSubsetOf
  • SetEquals

LINQメソッドとHashSet<T> "オーバーラップ"メソッドのもう1つの違いは、LINQが常に新しいIEnumerable<T>を返し、HashSet<T>メソッドがソースコレクションを変更することです。

1
c_buk

要するに、辞書(またはSがTのプロパティである辞書)を使用したいときはいつでも、HashSet(またはHashSet + Sに相当するIEquatableの実装)を検討する必要があります。

1
Addys