ToList()を呼び出すとパフォーマンスに影響がありますか?


139

を使用する場合ToList()、考慮する必要があるパフォーマンスへの影響はありますか?

私はディレクトリからファイルを取得するためのクエリを作成していました、それはクエリです:

string[] imageArray = Directory.GetFiles(directory);

しかし、List<>代わりに一緒に仕事をしたいので、私は中に入れることにしました...

List<string> imageList = Directory.GetFiles(directory).ToList();

それで、このような変換を行うことを決定するときに考慮すべき、または多数のファイルを処理するときにのみ考慮すべき、ある種のパフォーマンスへの影響がありますか?これは無視できる変換ですか?


ここでも答えを知りたいと思っています。私見アプリは、パフォーマンスクリティカルでない限り、私は私がいつも使用したいと思うList<T>の賛成でT[]、もちろん、変換がない限り、それはコードが複数の論理/リード/保守性(行った場合原因目立ち、私は再だろう、その場合にはパフォーマンスの問題を私が推測するそれをご覧ください)。
Sepster 2013年

配列からリストを作成すると、非常に安価になるはずです。
leppie 2013年

2
@Sepster仕事をするのに必要なだけ、具体的にデータ型を指定します。Addまたはを呼び出す必要がない場合はRemove、そのままIEnumerable<T>(またはそれ以上var
pswg

4
この場合、EnumerateFilesではなくを呼び出す方が良いと思うGetFilesので、配列は1つだけ作成されます。
tukaef 2013年

3
GetFiles(directory)、それは現在.NETで実装されているので、ほとんどを実行しますnew List<string>(EnumerateFiles(directory)).ToArray()。したがってGetFiles(directory).ToList()、リストを作成し、そこから配列を作成してから、もう一度リストを作成します。2kayが言うように、あなたはEnumerateFiles(directory).ToList()ここで行うことを好むはずです。
Joren

回答:


178

IEnumerable.ToList()

はい、IEnumerable<T>.ToList()パフォーマンスに影響します。これはO(n)操作ですが、パフォーマンスが重要な操作でのみ注意が必要になる可能性があります。

ToList()操作は、使用するList(IEnumerable<T> collection)コンストラクタを。このコンストラクターは配列のコピーを作成する必要があります(より一般的にはIEnumerable<T>)。そうしないと、元の配列の将来の変更がソースで変更され、T[]これも一般的に望ましくありません。

これを繰り返すと、大きなリストで違いが生じるだけです。メモリのチャンクのコピーは、実行するのに非常に高速な操作です。

便利なヒント、AsvsTo

あなたは、で始まるいくつかの方法があるLINQに気付くでしょうAs(などAsEnumerable())やTo(などがToList())。で始まるメソッドToは、上記のような変換を必要とします(つまり、パフォーマンスに影響を与える可能性があります)As

の詳細 List<T>

List<T>興味がある場合の動作の詳細を次に示します。

AはList<T>また、動的配列と呼ばれる構成を使用します。これはオンデマンドでサイズ変更する必要があります。このサイズ変更イベントは、古い配列の内容を新しい配列にコピーします。したがって、最初は小さく、必要に応じてサイズが大きくなります。

これは、CapacityとのCount属性の違いList<T>です。Capacity舞台裏の配列のサイズを指し、は常にCountであるアイテムの数です。したがって、項目がリストに追加され、それを超えて増加すると、のサイズが2倍になり、配列がコピーされます。List<T><= CapacityCapacityList<T>


2
List(IEnumerable<T> collection)コンストラクタがコレクションパラメータがあるかどうかをチェックICollection<T>し、必要なサイズの新しい内部配列をすぐに作成することを強調したかっただけです。パラメータコレクションがでない場合ICollection<T>、コンストラクタはそれを反復処理してAdd各要素を呼び出します。
Justinas Simanavicius

ToList()は、誤解を招くような要求の多い操作と見なされることが多いことに注意してください。これは、LINQクエリを使用してIEnumerable <>を作成すると発生します。linqクエリは作成されますが、実行されません。ToList()を呼び出すとクエリが実行されるため、リソースを大量に消費しているように見えますが、ToList()操作ではなく、クエリが集中的に実行されます(本当に巨大なリストでない限り)
dancer42

36

toList()を呼び出すときにパフォーマンスに影響はありますか?

はい、もちろん。理論的i++にはパフォーマンスに影響を与えることさえあり、それはおそらく数ティックの間プログラムを遅くします。

何をし.ToListますか?

を呼び出す.ToListと、コードが呼び出すEnumerable.ToList()拡張メソッドですreturn new List<TSource>(source)。対応するコンストラクターでは、最悪の場合、アイテムコンテナーを通過し、それらを1つずつ新しいコンテナーに追加します。したがって、その動作はパフォーマンスにほとんど影響しません。アプリケーションのパフォーマンスのボトルネックになることは不可能です。

問題のコードの何が問題になっていますか

Directory.GetFilesフォルダを通過し、すべてのファイルの名前をすぐにメモリに返します。string[]が大量のメモリを消費し、すべてが遅くなる可能性があります。

次に何をすべきか

場合によります。あなた(およびビジネスロジック)が、フォルダー内のファイル量が常に小さいことを保証している場合、コードは受け入れ可能です。ただしDirectory.EnumerateFiles、C#4では、遅延バージョンを使用することをお勧めします。これはクエリのようなもので、すぐには実行されません。次のようにクエリを追加できます。

Directory.EnumerateFiles(myPath).Any(s => s.Contains("myfile"))

これは、「myfile」を含む名前のファイルが見つかるとすぐにパスの検索を停止します。これは明らかにより良いパフォーマンスを持っています.GetFiles


19

toList()を呼び出すときにパフォーマンスに影響はありますか?

はいあります。拡張メソッドEnumerable.ToList()を使用するとList<T>IEnumerable<T>ソースコレクションから新しいオブジェクトが構築されます。もちろん、これはパフォーマンスに影響を与えます。

ただし、理解List<T>すると、パフォーマンスへの影響が大きいかどうかを判断するのに役立ちます。

List<T>配列(T[])を使用して、リストの要素を格納します。割り当てられた配列は拡張できないList<T>ため、サイズが大きすぎる配列を使用してリストの要素を格納します。ときにList<T>サイズを超えて成長する基盤となる配列は、新しい配列が割り当てられる必要があり、古い配列の内容は、リストが成長することができます前に、新しい大きな配列にコピーする必要があります。

List<T>からnew を構築する場合、IEnumerable<T>2つのケースがあります。

  1. ソースコレクションは次のものを実装しますICollection<T>。次に、ICollection<T>.Countを使用してソースコレクションの正確なサイズを取得し、を使用してソースコレクションのすべての要素をバッキング配列にコピーする前に、対応するバッキング配列を割り当てICollection<T>.CopyTo()ます。この操作は非常に効率的で、メモリのブロックをコピーするためのいくつかのCPU命令にマップされます。ただし、パフォーマンスに関しては、新しいアレイにはメモリが必要であり、すべての要素をコピーするにはCPUサイクルが必要です。

  2. それ以外の場合、ソースコレクションのサイズは不明であり、の列挙子をIEnumerable<T>使用して、各ソース要素を一度に1つずつ新しいに追加しList<T>ます。最初は、バッキング配列は空であり、サイズ4の配列が作成されます。次に、この配列が小さすぎるとサイズが2倍になるため、バッキング配列はこの4、8、16、32などのように大きくなります。バッキング配列が大きくなるたびに、再配置する必要があり、これまでに格納されたすべての要素をコピーする必要があります。この操作は、正しいサイズの配列をすぐに作成できる最初のケースに比べてはるかにコストがかかります。

    また、ソースコレクションに33個の要素が含まれている場合、リストは64個の要素の配列を使用してメモリを浪費します。

あなたの場合、ソースコレクションは実装する配列なICollection<T>ので、ソース配列が非常に大きくない限り、パフォーマンスへの影響は心配する必要はありません。呼び出すToList()と、単純にソース配列がコピーされ、List<T>オブジェクトにラップされます。2番目のケースのパフォーマンスでさえ、小さなコレクションでは心配する必要はありません。


5

「考慮すべきパフォーマンスへの影響はありますか?」

正確なシナリオの問題は、まず第一にパフォーマンスに関する本当の懸念は、ハードドライブの速度とドライブのキャッシュの効率にあるということです。

その観点から、影響がある点に確かにごくわずかであるNOそれは考慮する必要がありません。

ただし、List<>構造の機能をより生産的にしたり、アルゴリズムをより使いやすくしたり、その他の利点をもたらすために本当に必要な場合のみ。それ以外の場合は、理由もなく、意図的にわずかなパフォーマンスヒットを追加するだけです。その場合、当然、それを行うべきではありません。:)


4

ToList()新しいリストを作成し、それに要素を配置します。つまり、実行に関連するコストが発生しToList()ます。小さなコレクションの場合はそれほど大きなコストにはなりませんが、膨大なコレクションがあると、ToListを使用した場合にパフォーマンスが低下する可能性があります。

コレクションをリストに変換せずに実行している作業を実行できない場合を除き、通常はToList()を使用しないでください。たとえば、コレクションを反復処理するだけの場合は、ToListを実行する必要はありません。

LINQ to SQLを使用するデータベースなどのデータソースに対してクエリを実行している場合、遅延実行を行う代わりにLINQ to SQLでToListを使用すると、必要に応じてアイテムをロードするため、ToListを実行するコストがはるかに高くなります(これは有益な場合があります)多くのシナリオで)データベースからメモリにアイテムを即座にロードします


ハリス:元のソースについてわからないことToList()を呼び出した後に元のソースがどうなるか
TalentTuner

@Saurabh GCがクリーンアップします
pswg 2013年

@Saurabh元のソースには何も起こりません。元のソースの要素は、新しく作成されたリストによって参照されます
Haris Hasan

「コレクションを反復処理するだけの場合は、ToListを実行する必要はありません」-では、どのように反復処理すればよいでしょうか。
SharpC

4

それは次のように(非)効率的です:

var list = new List<T>(items);

をとるコンストラクタのソースコードを逆アセンブルIEnumerable<T>すると、いくつかのことが行われることがわかります。

  • を呼び出すcollection.Countので、の場合collectionは、IEnumerable<T>強制的に実行されます。collectionが配列、リストなどの場合はにする必要がありますO(1)

  • collection実装ICollection<T>している場合は、ICollection<T>.CopyToメソッドを使用して内部配列にアイテムを保存します。それはすべきことがO(n)あること、nコレクションの長さ。

  • collectionが実装されていない場合ICollection<T>、コレクションの項目を反復処理し、それらを内部リストに追加します。

したがって、はい。新しいリストを作成する必要があるため、より多くのメモリを消費します。最悪の場合は、O(n)を繰り返してcollection各要素のコピーを作成するため、になります。


3
閉じる、0(n)ここでn元のコレクションの文字列が占めるバイトの合計であり、要素の数ではありません(より正確には、n =バイト/ワードサイズ)
user1416420

@ user1416420私は間違っているかもしれませんが、なぜですか?何それは他のいくつかのタイプ(例えば。の集まりであるかboolintなど)?コレクション内の各文字列のコピーを作成する必要はありません。新しいリストに追加するだけです。
オスカーメデロス2013年

新しいメモリの割り当てとバイトのコピーは、このメソッドを無効にするものです。ブール値も.NETでは4バイトを占有します。実際、.NET内のオブジェクトの各参照は少なくとも8バイトの長さなので、かなり低速です。最初の4バイトはタイプテーブルを指し、2番目の4バイトは値または値を見つけるメモリの場所を
指し

3

ファイルリストの取得のパフォーマンスを考慮すると、ToList()無視できます。しかし、実際には他のシナリオではありません。それは本当にあなたがそれをどこで使っているかによります。

  • 配列、リスト、またはその他のコレクションを呼び出すときに、コレクションのコピーをとして作成しますList<T>。ここでのパフォーマンスは、リストのサイズによって異なります。本当に必要なときにそれをすべきです。

    あなたの例では、それを配列で呼び出します。配列を反復処理し、新しく作成したリストに1つずつ項目を追加します。したがって、パフォーマンスへの影響はファイルの数によって異なります。

  • を呼び出すと 、(通常はクエリ)IEnumerable<T>具体化されIEnumerable<T>ます。


2

ToList新しいリストを作成し、元のソースから新しく作成されたリストに要素をコピーするので、元のソースから要素をコピーするだけで、ソースのサイズに依存します

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