C#でHashtableよりもDictionaryが優先されるのはなぜですか?


1396

ほとんどのプログラミング言語では、ハッシュテーブルよりも辞書が優先されます。その背後にある理由は何ですか?


21
>これは必ずしも真実ではありません。ハッシュテーブルは、辞書の実装です。それは典型的なものであり、それは.NETのデフォルトの1つかもしれませんが、定義上唯一のものではありません。ECMA標準でこれが必要かどうかはわかりませんが、MSDNのドキュメントでは、ハッシュテーブルとして実装されていることを明確に示しています。代替案の方が妥当な場合のために、SortedListクラスも提供しています。
2009

15
@Promitは常にDictionaryの実装だと思っていましたHashtable
b1nary.atr0phy 2015年

2
その理由は、辞書でキーのタイプと自分の値を定義できるためだと思います。Hashtableはオブジェクトのみを取得でき、(object.GetHashCode()からの)ハッシュに基づいてペアを保存します。
ラジネーター2016

2
@Danあなたの主張はかなり間違っています...ハッシュテーブルには各キーの1つのインスタンスしか含まれておらず、検索で複数のエントリが得られることはありません。各キーに複数の値を関連付ける場合は、ハッシュテーブルの値を値のリストにします。「ディクショナリ」のようなデータ構造はありません...ディクショナリは、一部のライブラリがハッシュテーブルに使用する名前です。たとえば、C#の非ジェネリックハッシュテーブルはと呼ばれHashTableます。言語にジェネリックを追加したとき、ジェネリックバージョンと呼ばれていましたDictionary。どちらもハッシュテーブルです。
ジムBalter

3
@Danあなたの主張は間違っています...ハッシュテーブル(en.wikipedia.org/wiki/Hash_table)は辞書の特定の実装で、別名連想配列(en.wikipedia.org/wiki/Associative_array)であり、辞書、各キーの1つのインスタンスのみが含まれ、検索で複数のエントリが生成されることはありません。各キーに複数の値を関連付ける場合は、ハッシュテーブルの値を値のリストにします。また、.NET辞書とHashtableクラスはどちらもハッシュテーブルです。
ジムBalter

回答:


1568

価値があるのは、辞書(概念的には)ハッシュテーブルです。

「なぜDictionary<TKey, TValue>クラスの代わりにクラスを使用するのか」という意味の場合Hashtable、それは簡単な答えです。Dictionary<TKey, TValue>ジェネリック型であり、そうでHashtableはありません。つまりDictionary<TKey, TValue>、ランダムオブジェクトをオブジェクトに挿入できず、取り出した値をキャストする必要がないため、でタイプセーフになります。

興味深いことに、Dictionary<TKey, TValue>.NET Frameworkの実装は、Hashtableソースコードのこのコメントからわかるように、に基づいています。

一般的な辞書はHashtableのソースからコピーされました

ソース


393
また、ボクシング/アンボクシングがないため、ジェネリックコレクションの方がはるかに高速です
Chris S

6
わからない<T>それは本当だ上記のステートメントではなく、ArrayListのためのリストの対ハッシュテーブルについて
クリス・S

36
HashtableはObjectを使用して内部で物事を保持するため(一般的ではない方法のみ)、ボックス化/ボックス化解除も行う必要があります。
グバンテ2009

16
@BrianJ:「ハッシュテーブル」(2ワード)は、この種の構造のコンピュータサイエンス用語です。辞書は特定の実装です。HashTableは、Dictionary <object、object>にほぼ対応しています(ただし、インターフェイスはわずかに異なります)が、どちらもハッシュテーブルの概念の実装です。もちろん、問題をさらに混乱させるために、一部の言語ではハッシュテーブルを「ディクショナリ」(Pythonなど)と呼んでいますが、適切なCS用語は依然としてハッシュテーブルです。
Michael Madsen

32
@BrianJ:両方HashTable(クラス)とDictionary(クラス)は、ハッシュテーブル(概念)ですが、HashTableではないDictionary、またあるDictionaryA HashTable。これらは、非常によく似た方法で使用されDictionary<Object,Object>、aと同じ型なしの方法でHashTable動作できますが、コードを直接共有しません(パーツは非常によく似た方法で実装される可能性があります)。
Michael Madsen

625

Dictionary<<< >>>のHashtable違い:

  • ジェネリック <<< >>> 非ジェネリック
  • 独自のスレッド同期が必要<<< >>> メソッドを通じてスレッドセーフバージョンを提供Synchronized()
  • 列挙型アイテム:KeyValuePair<<< >>>列挙型アイテム:DictionaryEntry
  • 新しい(> .NET 2.0)<<< >>>古い(.NET 1.0以降)
  • であるSystem.Collections.Generic <<< >>>であるにSystem.Collections
  • 存在しないキーへのリクエストは例外 <<< >>>をスローします存在しないキーへのリクエストはnullを返します
  • 値型の場合は少し速くなる可能性があります <<< >>> 値型の場合は少し遅くなります(ボックス化/ボックス化解除が必要)

Dictionary/ Hashtable類似点:

  • どちらも内部的にはハッシュテーブルです ==キーに従って多くのアイテムのデータに高速にアクセスできます
  • どちらも不変で一意のキーが必要です
  • 両方のキーには独自のGetHashCode()メソッドが必要です

同様の .NETコレクション(辞書とハッシュテーブルの代わりに使用する候補):

  • ConcurrentDictionary- スレッドセーフ(複数のスレッドから同時に安全にアクセスできます)
  • HybridDictionary- 最適化されたパフォーマンス(少数のアイテムと多くのアイテム)
  • OrderedDictionary-値はintインデックスを介してアクセスできます(アイテムが追加された順序で)
  • SortedDictionary-アイテムは自動的にソートされます
  • StringDictionary-強く型付けされ、文字列用に最適化されています

11
@ Guillaume86、これが、代わりにTryGetValueを使用する理由ですmsdn.microsoft.com/en-us/library/bb347013.aspx
Trident D'Gao

2
StringDictionary... btwの+1は、デフォルトのコンストラクタを使用する場合StringDictionaryとは異なりDictionary<string, string>ます。
チェンチェン

ParallelExtensionsExtras @ code.msdn.microsoft.com / windowsdesktop / …には、優れたバインディングと同時実行性であるObservableConcurrentDictionaryが含まれています。
投票コーヒー2014年

3
素晴らしい説明、思い浮かぶかもしれない質問を減らすために類似点をリストアップしたことは本当に素晴らしいことです
mkb


178

のでDictionary、一般的なクラスが(あるDictionary<TKey, TValue>)、そのコンテンツにアクセスするようにすること(つまり、あなたがからキャストする必要はありません、タイプセーフであるObjectあなたがそうであるように、Hashtable)。

比較する

var customers = new Dictionary<string, Customer>();
...
Customer customer = customers["Ali G"];

var customers = new Hashtable();
...
Customer customer = customers["Ali G"] as Customer;

ただし、Dictionary内部的にはハッシュテーブルとして実装されるため、技術的には同じように機能します。


88

参考:.NETでは、Hashtable複数のリーダースレッドと1つの書き込みスレッドで使用してDictionaryもスレッドセーフですが、パブリック静的メンバーではスレッドセーフですが、インスタンスメンバーはスレッドセーフであるとは限りません。

Hashtableこのため、すべての辞書を元に戻す必要がありました。


10
楽しい。Dictionary <T>のソースコードは、非常にすっきりと高速に見えます。辞書を使用して独自の同期を実装することをお勧めします。ディクショナリの読み取りが最新である必要がある場合は、ディクショナリの読み取り/書き込みメソッドへのアクセスを同期するだけで済みます。それはたくさんのロックになるでしょうが、それは正しいでしょう。
Triynko、

10
あるいは、読み取りが完全に最新である必要がない場合は、辞書を不変として扱うことができます。次に、Dictionaryへの参照を取得し、読み取りをまったく同期しないことでパフォーマンスを上げることができます(それは不変であり、本質的にスレッドセーフであるため)。それを更新するには、バックグラウンドでディクショナリの完全に更新されたコピーを作成し、参照をInterlocked.CompareExchangeと交換するだけです(単一の書き込みスレッドを想定しています。複数の書き込みスレッドは更新の同期が必要です)。
Triynko

38
.Net 4.0 ConcurrentDictionaryは、すべてのpublic / protectedメソッドがスレッドセーフになるように実装されているクラスを追加しました。レガシープラットフォームをサポートする必要がない場合はHashtable、マルチスレッドコードのmsdn.microsoft.com/en-us/library/dd287191.aspx
Dan Is Fiddling By Firelight

救出に匿名。クールな答え。
unkulunkulu 2012年

5
HashTableは、情報がテーブルから削除されないシナリオでは、スレッドセーフなリーダーライターにすぎないことを読んだことを思い出します。別のアイテムが削除されているときにリーダーがテーブルにあるアイテムを要求し、リーダーがアイテムの複数の場所を調べる場合、リーダーがライターを検索している間にアイテムが移動する可能性があります。検査されていない場所から検査された場所へ、その結果、アイテムが存在しないという誤った報告が発生します。
スーパーキャット2013年

68

.NETでは、の違いDictionary<,>とは、HashTableあなたは、静的な型チェック(と減少ボクシングの面でジェネリック医薬品のすべての利点を得るように、前者は一般的なタイプは、ある主ということですが、人々は考える傾向として、これは大きな通りではありませんパフォーマンスの条件-ただし、ボクシングには明確なメモリコストがあります)。


34

辞書はハッシュテーブルと同じだと人々は言っています。

これは必ずしも本当ではありません。ハッシュテーブルは、辞書を実装する1つの方法です。その典型的なものであり、それはDictionaryクラスの.NETのデフォルトの1つかもしれませんが、定義上、唯一のものではありません。

リンクされたリストまたは検索ツリーを使用して、同じように辞書を実装することもできますが、これはそれほど効率的ではありません(いくつかの測定基準では)。


4
MSのドキュメントでは、「Dictionary <(Of <(TKey、TValue>)>)クラスがハッシュテーブルとして実装されているため、キーを使用した値の取得はO(1)に非常に速くなっています。」-したがって、を処理するときにハッシュテーブルを保証する必要がありますDictionary<K,V>IDictionary<K,V>ただし、何でも
かまい

13
@ rix0rrr-私はあなたがそれを逆に持っていると思います、辞書はHashTableを使用しますが、HashTableは辞書を使用しません。
ジョセフハミルトン

8
@JosephHamilton-rix0rrrはそれを正しく理解しました:「ハッシュテーブル辞書の実装です。」彼はクラスではなく「辞書」という概念を意味します(小文字に注意)。概念的には、ハッシュテーブルは辞書インターフェイスを実装します。.NETでは、Dictionaryはハッシュテーブルを使用してIDictionaryを実装します。面倒です;)
Robert Hensing 2013年

私が.NETで話していたのは、それが彼の返答で参照したものだからです。
ジョセフハミルトン

2
@JosephHamilton:implements(またはの実装)は、リモートでusesと同じことを意味することさえありません。まったく逆です。おそらく、彼が少し違った方法で(ただし、同じ意味で)「ハッシュテーブルは、ディクショナリを実装するための1つの方法である」と言った方が明確だったでしょう。つまり、ディクショナリの機能が必要な場合、そのための1つの方法(ディクショナリの実装)は、ハッシュテーブルを使用することです。
ToolmakerSteve 2015年

21

CollectionsGenericsは、オブジェクトのグループを処理するのに役立ちます。.NETでは、すべてのコレクションオブジェクトはインターフェイスの下にあり、インターフェイスIEnumerableにはArrayList(Index-Value))&がありHashTable(Key-Value)ます。.NETフレームワーク2.0の後、ArrayListHashTableに置き換えましたListDictionary。現在、ArraylistHashTableは現在のプロジェクトでは使用されていません。

HashTable&の違いはDictionaryDictionaryジェネリックでHastableはないジェネリックです。任意のタイプのオブジェクトをに追加できますHashTableが、取得中に必要なタイプにキャストする必要があります。したがって、タイプセーフではありません。しかしdictionary、それ自体を宣言するときに、キーと値のタイプを指定できるため、取得中にキャストする必要はありません。

例を見てみましょう:

ハッシュ表

class HashTableProgram
{
    static void Main(string[] args)
    {
        Hashtable ht = new Hashtable();
        ht.Add(1, "One");
        ht.Add(2, "Two");
        ht.Add(3, "Three");
        foreach (DictionaryEntry de in ht)
        {
            int Key = (int)de.Key; //Casting
            string value = de.Value.ToString(); //Casting
            Console.WriteLine(Key + " " + value);
        }

    }
}

辞書、

class DictionaryProgram
{
    static void Main(string[] args)
    {
        Dictionary<int, string> dt = new Dictionary<int, string>();
        dt.Add(1, "One");
        dt.Add(2, "Two");
        dt.Add(3, "Three");
        foreach (KeyValuePair<int, String> kv in dt)
        {
            Console.WriteLine(kv.Key + " " + kv.Value);
        }
    }
}

2
KeyValuePairのデータ型を明示的に割り当てる代わりに、varを使用できます。したがって、これはタイピングを減らすでしょう-foreach(var kv in dt)...単なる提案です。
Ron

16

辞書:

  • 存在しないキーを見つけようとすると例外を返す/スローします。

  • ボックス化とボックス化解除がないため、Hashtableより高速です。

  • パブリックスタティックメンバーのみがスレッドセーフです。

  • ディクショナリは一般的なタイプであり、任意のデータタイプで使用できます(作成時に、キーと値の両方のデータタイプを指定する必要があります)。

    例: Dictionary<string, string> <NameOfDictionaryVar> = new Dictionary<string, string>();

  • DictionayはHashtableのタイプセーフな実装でKeysありValues、強く型付けされています。

ハッシュ表:

  • 存在しないキーを見つけようとするとnullを返します。

  • ボクシングとアンボクシングが必要なため、ディクショナリよりも低速です。

  • Hashtableのすべてのメンバーはスレッドセーフであり、

  • ハッシュテーブルはジェネリック型ではありませんが、

  • ハッシュテーブルは緩やかに型付けされたデータ構造であり、任意の型のキーと値を追加できます。


「存在しないキーを見つけようとすると、例外が返されるかスローされます。」あなたは、使用しない場合Dictionary.TryGetValue
ジム・Balter

16

MSDNのC#を使用したデータ構造広範な調査では、衝突解決戦略にも違いがあると述べています 。

ハッシュテーブルクラスは、と呼ばれる技術を使用して再ハッシュ

再ハッシュは次のように機能します。ハッシュの異なる関数のセットH 1 ... H nがあり、ハッシュテーブルからアイテムを挿入または取得する場合、最初はH 1ハッシュ関数が使用されます。これが衝突につながる場合は、代わりにH 2が試行され、必要に応じてH nまで試行されます。

辞書は、チェーンと呼ばれる手法を使用します

再ハッシュでは、衝突が発生した場合、ハッシュが再計算され、ハッシュに対応する新しいスロットが試行されます。ただし、連鎖では、衝突を保持するために2次データ構造が使用されます。具体的には、ディクショナリの各スロットには、そのバケットにマップする要素の配列があります。衝突が発生した場合、衝突要素がバケットのリストの先頭に追加されます。


16

.NET Framework 3.5以降には、キーのみが必要で値は必要ない場合のHashSet<T>すべての長所を提供するもDictionary<TKey, TValue>あります。

したがって、aを使用しDictionary<MyType, object>、常に値をnullに設定してタイプセーフハッシュテーブルをシミュレートする場合は、への切り替えを検討する必要がありHashSet<T>ます。


14

Hashtableあなたがに任意のタイプのキーと値を追加できるように、緩やかに型付けされたデータ構造ですHashtableDictionaryクラスは、タイプセーフでHashtable実装、およびキーと値は強く型付けされています。Dictionaryインスタンスを作成するときは、キーと値の両方のデータ型を指定する必要があります。


11

MSDNによると、「Dictionary <(Of <(TKey、TValue>)>)クラスはHashTableとして実装されているのではなく、「Dictionary <(Of <(TKey、TValue>)>)クラスはハッシュテーブルとして実装されています

辞書はHashTableとして実装されていませんが、ハッシュテーブルの概念に従って実装されています。Genericsを使用しているため、実装はHashTableクラスとは関係ありませんが、内部ではMicrosoftが同じコードを使用し、Object型のシンボルをTKeyおよびTValueに置き換えた可能性があります。

.NET 1.0 Genericsには存在しませんでした。これは、HashTableとArrayListが最初に始まった場所です。


そのMSDNの引用を修正できますか?何かが欠けているか間違っています。文法的ではなく、ややわかりにくい。
Peter Mortensen 2017年

10

ハッシュ表:

キー/値は、ヒープへの格納中にオブジェクト(ボックス化)タイプに変換されます。

ヒープから読み取るときに、キー/値を目的のタイプに変換する必要があります。

これらの操作は非常にコストがかかります。ボクシング/アンボクシングはできるだけ避ける必要があります。

辞書: HashTableの一般的なバリアント。

ボクシング/アンボクシングはありません。変換は必要ありません。


8

Hashtableオブジェクトは、コレクションの要素を含むバケットで構成されています。バケットはHashtable内の要素の仮想サブグループであり、ほとんどのコレクションよりも簡単かつ高速に検索および取得できます。

Dictionaryクラスは、Hashtableクラスと同じ機能を備えています。特定のタイプ(オブジェクト以外)のディクショナリは、値タイプのHashtableよりもパフォーマンスが優れています。これは、Hashtableの要素がオブジェクトタイプであるため、通常、値タイプを格納または取得するときにボックス化とボックス化解除が発生するためです。

参考資料ハッシュテーブルと辞書のコレクション型


7

もう1つの重要な違いは、Hashtableはスレッドセーフであることです。Hashtableには、複数のリーダー/シングルライター(MR / SW)のスレッドセーフティが組み込まれているため、1つのライターを複数のリーダーと一緒にロックせずに使用できます。

辞書の場合、スレッドセーフはありません。スレッドセーフが必要な場合は、独自の同期を実装する必要があります。

さらに詳しく説明するには:

Hashtableは、Synchronizedプロパティを通じてスレッドセーフを提供します。プロパティは、コレクションのスレッドセーフラッパーを返します。ラッパーは、追加または削除操作のたびにコレクション全体をロックすることによって機能します。したがって、コレクションにアクセスしようとする各スレッドは、その順番が1つのロックを取得するまで待機する必要があります。これはスケーラブルではなく、大規模なコレクションのパフォーマンスを大幅に低下させる可能性があります。また、設計は競合状態から完全に保護されていません。

などの.NET Framework 2.0コレクションクラスはList<T>, Dictionary<TKey, TValue>、スレッド同期を提供しません。複数のスレッドでアイテムが同時に追加または削除された場合、ユーザーコードはすべての同期を提供する必要があります

タイプセーフとスレッドセーフが必要な場合は、.NET Frameworkで並行コレクションクラスを使用します。詳細はこちら

追加の違いは、ディクショナリに複数のエントリを追加するときに、エントリが追加される順序が維持されることです。辞書からアイテムを取得すると、レコードを挿入したときと同じ順序でレコードを取得します。一方、Hashtableは挿入順序を保持しません。


私が理解していることから、削除を含まないHashset使用シナリオ MR / SWスレッドの安全性が保証されます。MR / SWを完全に安全にすることを意図していたと思いますが、安全に削除を処理すると、MR / SWの安全性のコストが大幅に増加します。の設計はDictionaryMR / SWの安全性を削除なしのシナリオで最小限のコストで提供できたかもしれませんが、MSは削除なしのシナリオを「特別な」ものとして扱うことを避けたかったと思います。
スーパーキャット

5

私が理解できるもう1つの違いは次のとおりです。

Webサービスでは、Dictionary <KT、VT>(ジェネリック)は使用できません。その理由は、ジェネリック標準をサポートするWebサービス標準がないためです。


SOAPベースのWebサービスで汎用リスト(List <string>)を使用できます。ただし、ウェブサービスで辞書(またはハッシュテーブル)を使用することはできません。これの理由は、.net xmlserializerがディクショナリオブジェクトを処理できないためだと思います。
Siddharth

5

Dictionary<> ジェネリック型なので、型安全です。

HashTableに任意の値タイプを挿入できます。これにより、例外がスローされる場合があります。ただしDictionary<int>、整数値のみを受け入れ、同様Dictionary<string>に文字列のみを受け入れます。

したがって、のDictionary<>代わりに使用することをお勧めしますHashTable


0

ほとんどのプログラミング言語では、ハッシュテーブルよりも辞書が優先されます

私はこれが必ずしも本当であるとは思いません、ほとんどの言語は、彼らが好む用語に応じて、どちらか一方を持っています。

ただし、C#では、明らかな理由(私にとって)は、C#HashTablesおよびSystem.Collections名前空間の他のメンバーがほとんど使用されていないことです。それらはc#V1.1に存在していました。それらはC#2.0からSystem.Collections.Generic名前空間のGenericクラスに置き換えられました。


辞書を超えるハッシュテーブルの利点の1つは、キーが辞書に存在しない場合、エラーがスローされることです。キーがハッシュテーブルに存在しない場合は、nullを返します。
ビルノーマン

C.ではジェネリックの利点がないため、System.Collections.Hashtableの使用は避けます。キーが存在するかどうかわからない場合は、DictionaryのTryGetValueまたはHasKeyを使用できます。
kristianp

HasKeyではなく、おっと、ContainsKeyである必要があります。
kristianp

-3

.NET Reflectorを使用して私が見たものによると:

[Serializable, ComVisible(true)]
public abstract class DictionaryBase : IDictionary, ICollection, IEnumerable
{
    // Fields
    private Hashtable hashtable;

    // Methods
    protected DictionaryBase();
    public void Clear();
.
.
.
}
Take note of these lines
// Fields
private Hashtable hashtable;

したがって、DictionaryBaseがHashTableを内部で使用していることを確認できます。


16
System.Collections.Generic.Dictionary <TKey、TValue>は、DictionaryBaseから派生していません。
スネマーチ

「そのため、DictionaryBaseが内部でHashTableを使用していることを確認できます。」-それはいいですが、質問とは関係ありません。
ジムBalter
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.