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


134

HashSet<T>タイプを調べていますが、コレクションのどこにあるのかわかりません。

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

列挙のみですか?

回答:


228

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

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

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

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


1
フレームワークがSortedSetデータ構造を提供するという事実は、セットのプロパティではない順序についてのあなたの言うことと矛盾するか、または開発チームからの誤解を指摘します。
Veverke 2016

10
の項目の順序はHashSet定義されていないので、イテレータの順序に依存しないでください。セット内のアイテムに対して何かを行っているためにセットを反復する場合、注文に関連するものに依存していない限り、それは危険ではありません。A は、プラス注文のすべてのプロパティを持っていますが、から派生していません。言い換えると、SortedSetは個別のオブジェクトの順序付けられたコレクションですSortedSetHashSet SortedSetHashSet
キット

110

これが私が使用する実際の例ですHashSet<string>

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

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

24

A 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)操作に近くなります(リストはO(1)追加、O(1)インデックスによる取得、O(n)検索/削除する)。

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


14

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


21
Performance would be a bad reason to choose HashSet over List:私はあなたに同意しません。つまり、2つのリストではなくDictionrayを選択しても、パフォーマンスは向上しません。見てみましょう次の記事を
オスカーMederos

11
@オスカー:私はセットが速くないとは言いませんでした-私はそれがそれらを選択するための悪い根拠になると言いました。順序付けられたコレクションを表現しようとしている場合、セットは機能せず、シューホーンを実行するのは誤りです。必要なコレクションに順序がない場合、セットは完璧で、高速です。しかし、最初の質問は何が重要ですか。何を表現しようとしているのですか。
Carl Manaster、2011

2
しかし、それについて考えてください。あなたが与えられた文字列が10,000文字列のいくつかのコレクションのメンバーであるかどうかをチェックしておきたい場合は、技術的に、string[].ContainsそしてHashSet<string>.Contains自分の意図も同等に表現します。HashSetを選択する理由は、より高速に実行されるためです。
ケーシー

12

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

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


しかし、Setは単一の要素の取得を許可しませんか?セット[45]のように?
ジョーンベンジ

2
そのためには、メンバーセットを反復処理します。他の一般的な操作は、セットに要素が含まれているかどうかのチェック、またはセットのサイズの取得です。
アール

11

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

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"}を保持します。


6

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

また、セットは順序付けされていないため、セットにインデックスを付けることはできません。いくつかのアイテムを追加すると、セットは最初のものと2番目のものを覚えていません。


それらを反復するだけの場合、HashSetメソッドは、リストと比較してかなりのメモリ使用量を追加します。
SamuelWarren、

5

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

セットとリストの違いは、セット内に含まれる同じ要素を1回だけ許可することです。2番目の同じ要素を追加しようとすると、HashSet<T>単に戻りfalseます。実際、O(1)内部データ構造は単なるハッシュテーブルであるため、要素の検索は非常に高速です(時間)。

どちらを使用するか迷っているList<T>場合HashSet<T>は、where is appropiateの使用が最大の間違いではないことに注意してください。ただし、コレクション内に不要な重複アイテムがある場合に問題が発生する可能性があります。さらに、ルックアップ(アイテムの取得)は非常に効率的です-理想的にはO(1)(完全なバケット化のために)O(n)時間ではなく-多くのシナリオで非常に重要です。


1
セットに既存のアイテムを追加しても、例外はスローされません。Addは単にfalseを返します。また、技術的にハッシュルックアップは、完全なハッシュ関数がない限り、O(1)ではなくO(n)です。もちろん、実際には、ハッシュ関数が本当に悪い場合を除いて、O(1)であると想定することはできます。
sepp2k 2009

1
@ sepp2k:ええ、ブール値を返します...重要なのは、それが通知することです。そして、ハッシュルックアップは、バケット化がひどい場合の最悪の場合の O(n)です。一般に、O(1)にかなり近くなります。
ノルドリン2009

4

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

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

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


2
もう1つの注意点:セットでは、通常、要素の1つの出現のみが許可されます。
スティーブギディ

1

要するに、辞書(またはSがTのプロパティである辞書)を使いたくなったら、HashSet(またはHashSet + TにIEquatableを実装し、Sと同等)を検討する必要があります。


5
キーを気にしない限り、辞書を使用する必要があります。
Hardwareguy

1

基本的な想定シナリオでHashSet<T>は、LINQが提供するよりも2つのコレクションでより具体的な集合演算が必要な場合に使用する必要があります。LINQのようなメソッドDistinctUnionIntersectそしてExceptほとんどの状況で十分ですが、時にはあなたは、よりきめ細かな操作を必要とする、ともHashSet<T>用意されています。

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

LINQとの間にもう一つの違いHashSet<T>「オーバーラップ」の方法は、LINQは常に新しいを返すことでIEnumerable<T>、及びHashSet<T>方法は、ソースコレクションを変更します。

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