配列とList <T>のどちらを使用するか?


594
MyClass[] array;
List<MyClass> list;

一方が他方より望ましい場合のシナリオは何ですか?なぜ?


9
ここで人気のある議論で見られるように、配列はかなり時代遅れです。また、ここで、ブログのホストから指摘されました
ギメル2009年

9
私が間違っていない場合、List <>は内部構造として配列を持っています。内部配列がいっぱいになるたびに、コンテンツをサイズの2倍(または他の定数に現在のサイズを掛けたもの)の配列にコピーするだけです。en.wikipedia.org/wiki/Dynamic_array
Ykok

Ykok:あなたの言っていることは正しいようです。List <>のソースコードをここで見つけました。
Carol、

19
@gimel配列が時代遅れであると主張することはおそらく少し大胆です
awdz9nld

回答:


580

実際には、配列を使用することはまれです。List<T>配列のサイズ変更にはコストがかかるため、データを追加または削除したいときは必ず使用してください。データが固定長であることがわかっていて、非常に特定の理由(ベンチマーク後)でマイクロ最適化を行う場合は、配列が役立ちます。

List<T>LINQは配列よりもはるかに多くの機能を提供します(ただし、LINQはそれを少し均一化します)。ほとんどの場合、正しい選択です。paramsもちろん、引数を除いて。;-p

カウンターとして- List<T>一次元です。ここで、として次のような矩形(ETC)のアレイを有する有しint[,]又はstring[,,]-が、オブジェクトモデルで(必要な場合)、そのようなデータをモデル化する他の方法があります。

以下も参照してください。

それは私が作る、と多くの私の中のアレイの使用のいるProtobufネットプロジェクト。完全にパフォーマンスのために:

  • これは多くのビットシフトを行うので、a byte[]はエンコーディングにかなり不可欠です。
  • byte[]基礎となるストリーム(およびvv)に送信する前に満たすローカルローリングバッファーを使用します。他よりも速いBufferedStream;
  • サイズは一度作成されると固定され、非常に高速である必要があるため、内部ではオブジェクトFoo[]ではなくオブジェクトの配列ベースのモデルを使用しますList<Foo>

しかし、これは間違いなく例外です。一般的な基幹業務の処理では、List<T>毎回勝利します。


8
サイズ変更に関する議論は完全に有効です。ただし、サイズ変更が不要な場合でも、リストが好まれます。後者の場合、確かで論理的な議論はありますか、それとも「配列は時代遅れ」に過ぎませんか?
フレデリック

6
「配列のサイズ変更にはコストがかかるため、データを追加または削除するときはいつでも必ずList <T>を使用してください。」List <T>は内部的に配列を使用します。LinkedList <T>のことを考えていましたか?
dan-gph

14
より多くの機能==より複雑==あなたがそれらの機能を必要としない限り、良くない。この回答は基本的に、配列が優れている理由をリストしていますが、反対の結論を導き出します。
Eamon Nerbonne 2014年

12
@EamonNerbonneあなたがこれらの機能を使用していない場合、私は、彼らはあなたを傷つけるつもりはないされていることをほとんど保証をすることができます...しかし:突然変異を必要としませんコレクションの数が非常にあるものよりも、私の経験では、より小さな変異
マークグラベル

7
@MarcGravell:コーディングスタイルによって異なります。私の経験では、実質的にコレクションが変更されることはありません。あれは; コレクションはデータベースから取得されるか、何らかのソースから構築されますが、それ以降の処理は常に新しいコレクション(例:マップ/フィルターなど)を再作成することによって行われます。概念的な変更が必要な場合でも、新しいコレクションを生成するのが最も簡単な傾向があります。私はコレクションをパフォーマンスの最適化として変更するだけで、そのような最適化は非常にローカルであり、APIコンシューマーに変更を公開しない傾向があります。
Eamon Nerbonne 2014

121

私が驚いたリンクを追加するために答えるだけでは、まだ言及されていません。EricのLippertの「配列はやや有害だと考えられている」というブログエントリ

タイトルから、実用的な場所であればコレクションを使用するように提案していると判断できますが、Marcが正しく指摘しているように、配列が本当に唯一の実用的な解決策である場所はたくさんあります。


2
最後に、3年以上経ってこれを読んだ。良い記事、今良い記事。:)
スペンサー・ルポート2012

21

推奨する他の回答にもかかわらず、List<T>処理時には配列を使用する必要があります。

  • 画像ビットマップデータ
  • その他の低レベルのデータ構造(ネットワークプロトコルなど)

1
なぜネットワークプロトコルなのか?ここでカスタム構造を使用して、特別なシリアライザまたは明示的なメモリレイアウトを提供しませんか?さらに、List<T>バイト配列ではなくhere を使用することに反対することは何ですか?
Konrad Rudolph、

8
@Konrad-まあ、最初に、Stream.ReadとStream.Writeはbyte []で機能し、エンコーディングなども同様です
Marc Gravell

12

本当にパフォーマンスに関心がない限り、つまり「C ++ではなく.Netを使用しているのはなぜですか?」List <>を使用する必要があります。保守が簡単で、バックグラウンドで配列のサイズを変更するというすべての汚い作業を行います。(必要に応じて、List <>は配列サイズの選択に関して非常に優れているため、通常は必要ありません。)


15
「なぜC ++ではなく.Netを使用しているのですか?」XNA
Bengt

6

コレクション自体の不変性がクライアントとプロバイダーのコード間のコントラクトの一部である場合(必ずしもコレクション内のアイテムの不変性ではない)、およびIEnumerableが適切でない場合は、Listを優先して配列使用する必要があります。

例えば、

var str = "This is a string";
var strChars = str.ToCharArray();  // returns array

「strChars」を変更しても、「str」の基礎となる型に関する実装レベルの知識に関係なく、元の「str」オブジェクトは変更されないことは明らかです。

しかし、

var str = "This is a string";
var strChars = str.ToCharList();  // returns List<char>
strChars.Insert(0, 'X');

この場合、挿入メソッドが元の「str」オブジェクトを変更するかどうかは、そのコードスニペットだけでは明確ではありません。その決定を行うには、Stringの実装レベルの知識が必要であり、これはDesign by Contractアプローチを破ります。文字列の場合、それは大した問題ではありませんが、他のほとんどすべての場合に大問題になる可能性があります。リストを読み取り専用に設定すると効果がありますが、コンパイル時ではなく実行時エラーが発生します。


私はC#に比較的慣れていませんが、リストを返すと、配列を返さない方法で元のデータの可変性が示唆される理由がわかりません。名前がこのメソッドで始まるとTo、元のインスタンスを変更できないオブジェクトが作成されますが、strChars as char[]有効な場合は、元のオブジェクトを変更できるようになります。
Tim MB

@TimMBコレクションの不変性(アイテムを追加またはリモートからはできません)とコレクション内のアイテムの不変性があります。私は後者について言及していましたが、おそらくこの2つを混同しているでしょう。配列を返すと、項目を追加または削除できないことをクライアントに保証します。存在する場合は、配列を再割り当てし、元の配列に影響を与えないことが保証されます。リストを返すと、そのような保証は行われず、元のものが影響を受ける可能性があります(実装によって異なります)。コレクション内のアイテム(配列またはリスト)を変更すると、アイテムのタイプが構造体でない場合、元のアイテムに影響を与える可能性があります。
Herman Schoenfeld、

説明していただきありがとうございます。私はまだ混乱しています(おそらく、C ++の世界の出身だからです)。場合str、内部配列を使用し、ToCharArrayこの配列への参照を返すクライアントは変異することができるstr大きさが残って固定しても、その配列の要素を変更することによって。しかし、「strChars」を変更しても元の「str」オブジェクトが変更されないことは明らかです。ここで何が欠けていますか?私が見ることができることから、どちらの場合でも、クライアントは内部表現にアクセスでき、タイプに関係なく、これは何らかの種類の変更を許可します。
Tim MB

3

私は私が必要に行くよ正確にどのように多くの要素を知っている場合は、私は5つの要素しか必要と言う今まで、私は、配列を使用する5つの要素を。それ以外の場合は、List <T>を使用します。


1
要素の数がわかっている場合に、なぜList <T>を使用しないのですか?
オリバー

3

ほとんどの場合、a Listを使用するだけで十分です。List用途内部の配列は、そのデータを処理し、より多くの要素を追加するときに自動的に配列のサイズを変更するにはList、それがより簡単にあなたが事前に容量を知っている必要があり、配列、より使用できるようになり、その電流容量、より。

C#のリストまたは単に逆コンパイルの詳細については、http: //msdn.microsoft.com/en-us/library/ms379570(v=vs.80).aspx# datastructures20_1_topic5を参照してくださいSystem.Collections.Generic.List<T>

多次元データが必要な場合(たとえば、マトリックスを使用したり、グラフィックスプログラミングで)、おそらくarray代わりに使用します。

いつものように、メモリやパフォーマンスに問題がある場合は、それを測定してください!そうでなければ、コードについて誤った仮定をする可能性があります。


1
こんにちは、「リストのルックアップ時間がO(n)になる」が正しい理由を説明できますか?私が知る限り、List <T>は舞台裏で配列を使用しています。
トンボ

1
@dragonflyあなたは完全に正しいです。ソース。当時、私は実装がポインターを使用すると想定していましたが、それ以来、私は他の方法で学びました。上記のリンクから: 'このプロパティの値の取得はO(1)操作です。プロパティの設定もO(1)操作です。
Sune Rievers 2012

2

配列対 リストは、保守性とパフォーマンスの古典的な問題です。ほとんどすべての開発者が従う経験則では、両方を狙うべきですが、両者が衝突する場合は、パフォーマンスよりも保守性を選択してください。そのルールの例外は、パフォーマンスが問題であることがすでに証明されている場合です。この原則を配列と比較すると、リスト、そしてあなたが得るものはこれです:

パフォーマンスの問題が発生するまで、強く型付けされたリストを使用してください。パフォーマンスの問題が発生した場合は、アレイへのドロップアウトがソリューションのパフォーマンスにメリットをもたらすか、メンテナンスの面でソリューションに悪影響を与えるかどうかを決定します。


1

まだ言及されていないもう1つの状況は、多数の項目があり、それぞれが関連しているが独立している変数の固定された束で構成されている場合です(たとえば、点の座標または3D三角形の頂点)。公開フィールド構造の配列により、その要素を「インプレース」で効率的に変更できます。これは、他のコレクションタイプでは不可能なことです。構造体の配列はその要素をRAMに連続して保持するため、配列要素への順次アクセスは非常に高速です。コードが配列を介して多くの順次パスを作成する必要がある状況では、構造の配列が、クラスオブジェクト参照の配列または他のコレクションよりも2:1だけ優れている場合があります。さらに、

配列はサイズ変更できませんが、使用中の要素の数とともに配列参照をコードに格納し、必要に応じて配列をより大きな配列に置き換えることは難しくありません。あるいは、動作が非常に似ているList<T>がバッキングストアを公開するタイプのコードを簡単に記述して、MyPoints.Add(nextPoint);またはのどちらかを言うことができますMyPoints.Items[23].X += 5;。コードがリストの最後を超えてアクセスしようとした場合、後者は必ずしも例外をスローしないことに注意してください。ただし、使用法は概念的にはと非常に似ていList<T>ます。


あなたが説明したのはリスト<>です。インデクサーがあるため、基になる配列に直接アクセスでき、List <>がサイズを維持します。
Carl

@Carl:たとえばが与えられたPoint[] arr;場合、コードが言うことが可能ですarr[3].x+=q;。たとえばList<Point> list、代わりに言う必要があるでしょうPoint temp=list[3]; temp.x+=q; list[3]=temp;List<T>方法があったら助かりますUpdate<TP>(int index, ActionByRefRef<T,TP> proc, ref TP params)。そしてコンパイラは回すことができるlist[3].x+=q;{list.Update(3, (ref int value, ref int param)=>value+=param, ref q);はなく、そのような機能は存在しません。
スーパーキャット'15

朗報です。できます。list[0].X += 3;リストの最初の要素のXプロパティに3を追加します。And listis List<Point>and and Pointa class with X and Y properties
Carl

1

.NETのリストは配列のラッパーであり、内部で配列を使用します。リストの操作の時間の複雑さは配列の場合と同じですが、追加されたすべての機能/リストの使いやすさ(自動サイズ変更やリストクラスに付属するメソッドなど)により、オーバーヘッドが少し増えます。非常に最適化されたコードを記述する必要がある場合や、配列を中心に構築された他のコードを使用している場合など、やむを得ない理由がない限り、すべての場合にリストを使用することをお勧めします。


0

各データ型の機能を比較するのではなく、最も実用的な答えは「違いは、おそらく両方とも実装されているIEnumerableため、達成する必要があるものにとっておそらくそれほど重要ではないので、一般的な慣習に従って、Listしない理由があるまでは、その時点でおそらくで配列を使用する理由があるでしょうList。」

ほとんどの場合、マネージコードでは、マイクロ最適化を心配するよりも、コレクションをできるだけ簡単に操作できるようにしたいとします。


0

人気がないかもしれませんが、私はゲームプロジェクトの配列のファンです。-場合によっては、反復速度が重要になることがあります。要素ごとに多くのことを行わない場合、配列のforeachのオーバーヘッドは大幅に少なくなります-ヘルパー関数を使用すると、追加と削除はそれほど難しくありません-遅いですが、一度だけビルドする場合それは問題ではないかもしれません-ほとんどの場合、余分なメモリの浪費が減ります(構造体の配列では本当に重要です)-ガベージとポインタとポインタ追跡のわずかな減少

そうは言っても、実際には配列よりもリストをはるかに頻繁に使用しますが、それぞれの場所があります。

ラッパーと列挙のオーバーヘッドを最適化できるように、組み込み型のListがあると便利です。


0

リストの生成は配列よりも簡単です。配列の場合、データの正確な長さを知る必要がありますが、リストの場合、データサイズは任意です。また、リストを配列に変換できます。

List<URLDTO> urls = new List<URLDTO>();

urls.Add(new URLDTO() {
    key = "wiki",
    url = "https://...",
});

urls.Add(new URLDTO()
{
    key = "url",
    url = "http://...",
});

urls.Add(new URLDTO()
{
    key = "dir",
    url = "https://...",
});

// convert a list into an array: URLDTO[]
return urls.ToArray();

0

誰も言及していないため:C#では、配列はリストです。MyClass[]そして、List<MyClass>の両方を実装IList<MyClass>。(例:またはのvoid Foo(IList<int> foo)ように呼び出すことができます)Foo(new[] { 1, 2, 3 })Foo(new List<int> { 1, 2, 3 })

したがって、List<MyClass>引数としてaを受け入れるが機能のサブセットのみを使用するメソッドを作成している場合はIList<MyClass>、呼び出し元の便宜のために代わりにとして宣言することができます。

詳細:


「C#では、配列はリストです」これは正しくありません。配列はでなくListIListインターフェースを実装するだけです。
Rufus L

-1

データ構造が必要とされるコンテキストに完全に依存します。たとえば、Listを使用して他の機能またはサービスで使用されるアイテムを作成する場合は、それを行うのに最適な方法です。

アイテムのリストがあり、それらを表示したいだけの場合は、Webページの配列に、使用する必要のあるコンテナがあるとします。


1
アイテムのリストがあり、それらを表示したいだけの場合、すでに持っているリストを使用するだけで何が問題になっていますか?ここでアレイは何を提供しますか?
Marc Gravell

1
そして、「他の関数またはサービスで使用されるアイテムを作成する」ためには、実際には、イテレーターブロックを使用した方がいいIEnumerable<T>です。
Marc Gravell

-1

その場でキャストを行う能力について言及する価値があります。

interface IWork { }
class Foo : IWork { }

void Test( )
{
    List<Foo> bb = new List<Foo>( );
    // Error: CS0029 Cannot implicitly convert type 'System.Collections.Generic.List<Foo>' to 'System.Collections.Generic.List<IWork>'
    List<IWork> cc = bb; 

    Foo[] bbb = new Foo[4];
    // Fine
    IWork[] ccc = bbb;
}

そのため、Arrayを関数の戻り値の型または引数で使用すると、柔軟性が少し向上します。

IWork[] GetAllWorks( )
{
    List<Foo> fooWorks = new List<Foo>( );
    return fooWorks.ToArray( ); // Fine
}

void ExecuteWorks( IWork[] works ) { } // Also accept Foo[]

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