web-dev-qa-db-ja.com

.NETデータ構造:ArrayList、List、HashTable、Dictionary、SortedList、SortedDictionary-速度、メモリ、それぞれを使用するタイミング

.NETには多くの複雑なデータ構造があります。残念ながら、それらのいくつかは非常に似ており、いつ使用するか、いつ使用するかが常にわからない。私のC#とVisual Basicの本のほとんどはある程度それらについて語っていますが、実際の詳細には決して触れません。

Array、ArrayList、List、Hashtable、Dictionary、SortedList、およびSortedDictionaryの違いは何ですか?

列挙可能なのはどれですか(IList-「foreach」ループを実行できます)?キー/値ペア(IDict)を使用するのはどれですか?

メモリフットプリントはどうですか?挿入速度?検索速度?

言及する価値のある他のデータ構造はありますか?

メモリ使用量と速度(Big-O表記)の詳細をまだ探しています。

209
Pretzel

私の頭の上から:

  • Array *-昔ながらのメモリ配列を表します-通常のtype[]配列のエイリアスのようなものです。列挙できます。自動的に成長することはできません。私は非常に速い挿入と検索速度を仮定します。

  • ArrayList-自動的に成長する配列。より多くのオーバーヘッドが追加されます。列挙できます。おそらく通常の配列よりも低速ですが、それでもかなり高速です。これらは.NETでよく使用されます

  • List-私のお気に入りの1つ-ジェネリックで使用できるので、強く型付けされた配列を持つことができます。 List<string>。それ以外は、ArrayListのように動作します

  • Hashtable-プレーンな古いハッシュテーブル。 O(1)からO(n)最悪の場合。値とキーのプロパティを列挙し、キーと値のペアを実行できます

  • Dictionary-Dictionary<string, string>などのジェネリックを介してのみ厳密に型指定された上記と同じ

  • SortedList-ソートされた汎用リスト。物を置く場所を把握しなければならないため、挿入が遅くなりました。列挙することができます。おそらく検索時に同じです。再ソートする必要はありませんが、削除は通常の古いリストよりも遅くなります。

私はListDictionaryを常に使用する傾向があります-ジェネリックで強く型付けして使用し始めると、標準の非ジェネリックに戻すのは本当に難しいです。

他にも多くのデータ構造があります-興味深いことをするために使用できるKeyValuePairがあり、SortedDictionaryもあります。

148
Sam Schutte

可能な場合はジェネリックを使用します。これには以下が含まれます。

  • ArrayListの代わりのリスト
  • HashTableの代わりの辞書
28
Adam Tegen

まず、.NETのすべてのコレクションはIEnumerableを実装します。

第二に、ジェネリックがフレームワークのバージョン2.0で追加されたため、コレクションの多くが重複しています。

したがって、一般的なコレクションは機能を追加する可能性がありますが、大部分は次のとおりです。

  • ListはArrayListの一般的な実装です。
  • 辞書はHashtableの一般的な実装です

配列は、特定のインデックスに格納されている値を変更できる固定サイズのコレクションです。

SortedDictionaryは、キーに基づいてソートされるIDictionaryです。 SortedListは、必要なIComparerに基づいてソートされるIDictionaryです。

したがって、IDictionary実装(KeyValuePairsをサポートするもの)は次のとおりです。* Hashtable * Dictionary * SortedList * SortedDictionary

.NET 3.5で追加された別のコレクションは、ハッシュセットです。これは、集合演算をサポートするコレクションです。

また、LinkedListは標準のリンクリスト実装です(リストは、より高速に取得できるように配列リストです)。

24
Abe Heidebrecht

優れた虎の巻 データ構造、アルゴリズムなどの複雑さについて言及する.

21
Krishna

一般的なヒントをいくつか紹介します。

  • foreachを実装する型でIEnumerableを使用できます。 IListは、本質的にIEnumberableおよびCount(ゼロから始まるインデックスを使用してアイテムにアクセス)プロパティを持つItemです。一方、IDictionaryは、ハッシュ可能なインデックスでアイテムにアクセスできることを意味します。

  • ArrayArrayList、およびListはすべてIListを実装します。 DictionarySortedDictionary、およびHashtableは、IDictionaryを実装します。

  • .NET 2.0以降を使用している場合は、前述のタイプの一般的な対応物を使用することをお勧めします。

  • これらのタイプのさまざまな操作の時間とスペースの複雑さについては、それらのドキュメントを参照してください。

  • .NETデータ構造はSystem.Collections名前空間にあります。 PowerCollections などの追加のデータ構造を提供するタイプライブラリがあります。

  • データ構造を完全に理解するには、 CLRS などのリソースを参照してください。

18
blackwing

.NETデータ構造:

ArrayListとListが実際に異なる理由についての会話の詳細

配列

1人のユーザーが述べているように、配列は「古い学校」のコレクションです(はい、配列はSystem.Collectionsの一部ではありませんがコレクションと見なされます)。しかし、他のコレクション、つまりタイトルにリストしたもの(ここでは、ArrayListとList(Of T))と比較して、配列についての「古い学校」とは何ですか?配列を見て、基本から始めましょう。

まず、Microsoft .NETの Arrays は、「複数の[論理的に関連する]アイテムを単一のコレクションとして扱うことができるメカニズム」です(リンクされた記事を参照)。どういう意味ですか?配列は、個々のメンバー(要素)を順番に、開始アドレスと共にメモリに順番に格納します。配列を使用することにより、そのアドレスから順番に格納されている要素に簡単にアクセスできます。

それを超えて、プログラミング101の一般的な概念に反して、配列は実際には非常に複雑になる可能性があります。

配列は、1次元、多次元、またはジャッド(ジャグ配列については読む価値があります)です。配列自体は動的ではありません。初期化されると、nサイズの配列はn個のオブジェクトを保持するのに十分なスペースを確保します。配列内の要素の数は増減できません。 Dim _array As Int32() = New Int32(100)は、配列が100個のInt32プリミティブ型オブジェクトを格納するのに十分なメモリブロック上のスペースを予約します(この場合、配列は0を含むように初期化されます)。このブロックのアドレスは_arrayに返されます。

記事によると、 共通言語仕様 (CLS)では、すべての配列がゼロベースである必要があります。 .NETの配列は、ゼロ以外の配列をサポートしています。ただし、これはあまり一般的ではありません。ゼロベースのアレイの「共通性」の結果、Microsoftはパフォーマンスの最適化に多くの時間を費やしました;したがって、単一次元のゼロベース(SZ)配列は「特殊」であり、実際には(多次元などとは対照的に)配列の最良の実装です。SZには、それらを操作するための特定の中間言語命令があるためです。

配列は常に参照で渡されます(メモリアドレスとして)-知っておくべき配列パズルの重要な部分です。境界チェックを行います(エラーをスローします)が、配列の境界チェックを無効にすることもできます。

繰り返しますが、配列の最大の障害は、サイズ変更ができないことです。それらには「固定」容量があります。 ArrayListとList(Of T)の歴史への紹介:

ArrayList-非ジェネリックリスト

ArrayListList(Of T)とともに-いくつかの重大な違いはありますが、ここでは後で説明します) 広義では)コレクションへの次の追加としておそらく最もよく考えられます。 ArrayListは、---(IList (「ICollection」の子孫)インターフェースを継承します。 ArrayLists自体は bulkier -Listsより多くの overhead -を必要とします。

IListを使用すると、実装はArrayListsを固定サイズのリスト(Arraysなど)として処理できます。ただし、ArrayListによって追加された追加の機能を超えて、この場合のArrayLists(Arrays)は著しく遅いため、サイズが固定されているArrayListsを使用することには実際の利点はありません。

私の読書から、ArrayListsはギザギザにすることはできません:「要素としての多次元配列の使用はサポートされていません...」繰り返しますが、ArrayListsのcoの中に別の釘があります。 ArrayListも「型指定」されていません。つまり、すべての下にあるArrayListは、オブジェクトの動的配列であるObject[]です。これには、ArrayListsを実装するときに多くのボクシング(暗黙的)とボックス化解除(明示的)が必要になり、それらのオーバーヘッドが増加します。

根拠のない考え:私は、ArrayListsがArraysからList-type Collectionsに移動しようとする試みのやつ概念的な子の一種であると読んだり、教授から聞いたことを覚えていると思います。配列の大幅な改善であり、コレクションに関してさらなる開発が行われているため、もはや最良の選択肢ではありません

List(Of T):ArrayListになったもの(となることを望んでいたもの)

メモリ使用量の違いは、List(Of Int32)が同じプリミティブ型を含むArrayListよりも56%少ないメモリを消費するほど大きくなります(上記の紳士のリンクされたデモでは、8 MB対19 MB:再び、リンク here )-これは64ビットマシンによって複合された結果ですが。この違いは、2つのことを実際に示しています。最初の(1)、ボックス化されたInt32型の「オブジェクト」(ArrayList)は、純粋なInt32プリミティブ型(List)よりもはるかに大きいです。 2番目の(2)、64ビットマシンの内部動作の結果として、差は指数関数的です。

それで、違いは何ですか? List(Of T)MSDN は、「...インデックスによってアクセスできるオブジェクトの強く型付けされたリスト」としてList(Of T)を定義します。ここで重要なのは、「強く型付けされた」ビットです。List(Of T)は型を「認識」し、オブジェクトを型として保存します。したがって、Int32は、Object型ではなく、Int32として保存されます。これにより、ボックス化とボックス化解除に起因する問題がなくなります。

MSDNは、参照型ではなくプリミティブ型を格納するときにのみこの違いが作用することを指定しています。 あまりにも、違いは実際に大規模に発生します:500を超える要素。さらに興味深いのは、MSDNのドキュメントに記載されている「ArrayListクラスを使用する代わりに、List(Of T)クラスの型固有の実装を使用することはあなたにとって有利です...」

基本的に、List(Of T)はArrayListですが、優れています。これは、ArrayListの「一般的な同等物」です。 ArrayListと同様に、ソートされるまでソートされることは保証されません(図を参照)。 List(Of T)には、いくつかの追加機能もあります。

7
Thomas

私は質問に共感します-私も選択肢を見つけました(見つけますか?)当惑しているので、どのデータ構造が最も速いかを科学的に調べました(VBを使用してテストしましたが、C#は同じだと思います、両方の言語CLRレベルで同じことを行います)。 ここで私が行ったベンチマーク結果 (どの状況でどのデータ型を使用するのが最適かについての議論もあります)を見ることができます。

5
Andy Brown

ジェネリックコレクションは、特に多くのアイテムを反復処理する場合に、非ジェネリックコレクションよりもパフォーマンスが向上します。これは、ボックス化とボックス化解除が発生しなくなったためです。

3
Russ Cam

ハッシュテーブル/辞書はO(1)パフォーマンスです。つまり、パフォーマンスはサイズの関数ではありません。知っておくことが重要です。

編集:実際には、Hashtable/Dictionary <>ルックアップの平均時間の複雑さはO(1)です。

3
Chris

彼らはインテリセンスでかなりうまく綴られています。 System.Collections。またはSystem.Collections.Generics(推奨)と入力するだけで、利用可能なもののリストと簡単な説明が表示されます。

3
Joel Coehoorn

高頻度の体系的な取引エンジニアリングのためのハッシュテーブルと辞書に関する重要な注意:スレッドの安全性の問題

Hashtableは、複数のスレッドで使用できるスレッドセーフです。辞書のpublic staticメンバーはスレッドセーフですが、どのインスタンスメンバーもそうであるとは限りません。

そのため、Hashtableはこの点で「標準」の選択肢のままです。

2
Rob

最も人気のあるC#データ構造とコレクション

  • 配列
  • ArrayList
  • リスト
  • LinkedList
  • 辞書
  • HashSet
  • スタック
  • キュー
  • SortedList

C#.NETにはさまざまなデータ構造があります。たとえば、最も一般的なものの1つは配列です。ただし、C#にはさらに多くの基本的なデータ構造が付属しています。使用する正しいデータ構造を選択することは、適切に構造化された効率的なプログラムを作成することの一部です。

この記事では、C#.NET 3.5で導入された新しいものを含め、組み込みのC#データ構造について説明します。これらのデータ構造の多くは、他のプログラミング言語にも適用されることに注意してください。

配列

おそらく最も単純で最も一般的なデータ構造は配列です。 C#配列は、基本的にオブジェクトのリストです。その定義特性は、すべてのオブジェクトが同じタイプであり(ほとんどの場合)、特定の数があるということです。配列の性質により、リスト(インデックスとも呼ばれます)内の位置に基づいて、要素に非常に高速にアクセスできます。 C#配列は次のように定義されます。

[object type][] myArray = new [object type][number of elements]

いくつかの例:

 int[] myIntArray = new int[5];
 int[] myIntArray2 = { 0, 1, 2, 3, 4 };

上記の例からわかるように、配列は要素なしで、または既存の値のセットから初期化できます。配列に値を挿入するのは、適合する限り簡単です。配列のサイズよりも多くの要素がある場合、操作はコストがかかり、その時点で配列を拡張する必要があります。既存のすべての要素を新しい大きな配列にコピーする必要があるため、これには時間がかかります。

ArrayList

C#データ構造体ArrayListは、動的配列です。つまり、ArrayListは任意の量のオブジェクトと任意の型を持つことができます。このデータ構造は、新しい要素を配列に追加するプロセスを簡素化するために設計されました。内部では、ArrayListは、スペースがなくなるたびにサイズが2倍になる配列です。内部配列のサイズを2倍にすることは、長期的に要素コピーの量を減らす非常に効果的な戦略です。ここではその証拠にはなりません。データ構造は非常に簡単に使用できます。

    ArrayList myArrayList = new ArrayList();
    myArrayList.Add(56);
    myArrayList.Add("String");
    myArrayList.Add(new Form());

ArrayListデータ構造の欠点は、取得した値を元の型に戻す必要があることです。

int arrayListValue = (int)myArrayList[0]

ここにあるソースと詳細情報

1
leonidaa

実際、 MSDN はこれらすべての質問に対してかなり良い答えを提供するのに役立つと思います。 .NETコレクションを検索するだけです。

1
Scott

ジェネリックコレクションと非ジェネリックコレクションには、微妙な違いがあります。それらは単に異なる基になるデータ構造を使用するだけです。たとえば、Hashtableは1人のライターが複数のリーダーを同期なしで保証します。辞書にはありません。

1
Ilya Ryzhenkov