164

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

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

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

List<>しかし、代わりに一緒に仕事をするのが好きなので、入れることにしました...

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

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

4

8 に答える 8

199

IEnumerable.ToList()

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

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

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

便利なヒント、AsvsTo

AsLINQには、 (などAsEnumerable())およびTo(など)で始まるメソッドがいくつかあることに気付くでしょうToList()。で始まるメソッドはTo上記のような変換を必要とし(つまり、パフォーマンスに影響を与える可能性があります)、で始まるメソッドはAsキャストまたは単純な操作を必要とせず、単に必要とします。

に関する追加の詳細List<T>

List<T>興味がある場合の仕組みについてもう少し詳しく説明します:)

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

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

于 2013-03-20T07:20:00.377 に答える
42

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

于 2013-03-20T06:43:06.960 に答える
22

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

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

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

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

から新しいList<T>ものを構築する場合、 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番目のケースのパフォーマンスでさえ、小さなコレクションでは心配する必要はありません。

于 2013-03-20T07:32:03.807 に答える
5

次のように(非)効率的になります。

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各要素のコピーを作成するためにを繰り返すため、メモリを消費します。

于 2013-03-20T06:35:48.247 に答える
5

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

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

その観点から、影響は確かに無視できる程度であり、それを考慮する必要はありません。

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

于 2013-03-20T07:12:29.003 に答える
4

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

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

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

于 2013-03-20T06:22:55.413 に答える
3

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

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

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

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

于 2013-03-20T06:23:42.417 に答える
2

ToListは、新しいリストを作成し、元のソースから新しく作成されたリストに要素をコピーするため、元のソースから要素をコピーするだけで、ソースのサイズによって異なります。

于 2013-03-20T06:25:13.877 に答える