web-dev-qa-db-ja.com

キーの高速検索だけが必要で、値が関係ない場合は、C#辞書を使用する必要がありますか?

エントリを挿入して、エントリがすでに挿入されているかどうかをすばやく判断できるデータ型が必要です。 Dictionaryはこのニーズに適しているようです(例を参照)。しかし、私は辞書のvaluesを使用していません。それでも辞書を使用する必要がありますか、それともより適切なデータ型がありますか?

public class Foo
{
    private Dictionary<string, bool> Entities;

    ...

    public void AddEntity(string bar)
    {
        if (!Entities.ContainsKey(bar))
        {
            // bool value true here has no use and is just a placeholder
            Entities.Add(bar, true);
        }
    }

    public string[] GetEntities()
    {
        return Entities.Keys.ToArray();
    }

}
41
reformed

HashSet<T>

HashSet<T>クラスは、高性能な集合演算を提供します。セットとは、重複する要素が含まれておらず、要素が特定の順序になっていないコレクションです。

84
Habib

ハビブの答え は優れていますが、マルチスレッド環境でHashSet<T>を使用する場合、その結果、locksを使用してアクセスを保護する必要があります。 lockステートメントを使用してデッドロックを作成する傾向があります。また、locksは アムダールの法則 に従ってスピードアップを悪化させます。これは、lockステートメントを追加すると、実際に並列であるコードの割合が減少するためです。

これらの理由により、a ConcurrentDictionary<T,object> はマルチスレッド環境の要件に適合します。使用する場合は、質問で行ったようにラップしてください。値は重要ではないので、必要に応じてnew up objectsを必要に応じてトスしてください。 ソースコードlockステートメントがないことを確認できます

コレクションの可変性が必要なかった場合、これは意味がありません。しかし、あなたの質問は、あなたがAddEntityメソッドを持っているので、あなたがそれを必要とすることを意味します。

追加情報2017-05-19-実際には、ConcurrentDictionarydoes内部でロックを使用します、ただしlockステートメント自体ではありません-Monitor.Enterを使用します( TryAddInternal を確認してください方法)。ただし、ディクショナリ内の個々のバケットがロックされるようです。つまり、lockステートメントに全体を配置するよりも競合が少なくなります。

したがって、全体として、ConcurrentDictionaryはマルチスレッド環境に適しています。

Interlocked メソッドのみを使用して並行ハッシュセットを作成することは、実際には非常に困難です(不可能ですか?)。私は自分で試してみましたが、同時に2つのことを変更する必要があるという問題に遭遇し続けました-一般的にロックだけができること。私が見つけた1つの回避策は、ハッシュバケットに単一リンクリストを使用し、1つのスレッドが他のスレッドからの干渉なしにノードを操作する必要がある場合に、リストにサイクルを意図的に作成することでした。これにより、そのノードがノードで実行されてサイクルが元に戻されるまで、他のスレッドが同じ場所でスピンし続けます。もちろん、技術的にはロックを使用していませんが、適切に拡張できませんでした。

3
Matt Thomas