.Net 3.5(C#)を使用していますが、C#のパフォーマンスを聞いたことがありますList<T>.ToArray
は「悪い」です。これは、すべての要素のメモリがコピーされて新しい配列が形成されるためです。本当?
いいえ、それは真実ではありません。すべての要素(*)をメモリコピーして新しい配列を形成するだけなので、パフォーマンスは良好です。
もちろん、それはあなたが「良い」または「悪い」パフォーマンスとして定義するものに依存します。
(*)参照型の参照、値型の値。
[〜#〜]編集[〜#〜]
あなたのコメントに応えて、Reflectorを使用することは実装をチェックする良い方法です(以下を参照)。または、それをどのように実装するかについて数分間考えて、Microsoftのエンジニアがもっと悪い解決策を思い付かないことを信頼してください。
public T[] ToArray()
{
T[] destinationArray = new T[this._size];
Array.Copy(this._items, 0, destinationArray, 0, this._size);
return destinationArray;
}
もちろん、「良い」または「悪い」パフォーマンスは、いくつかの代替案に関連する意味しかありません。特定のケースで、測定可能なほど高速な目標を達成するための代替手法がある場合は、パフォーマンスが「悪い」と見なすことができます。そのような代替手段がない場合、パフォーマンスは「良好」(または「十分に良好」)です。
編集2
コメントへの応答:「オブジェクトの再構築はありませんか?」 :
参照型の再構築はありません。値型の場合、値はコピーされます。これは、大まかに再構成として説明できます。
ToArray()を呼び出す理由
ToArray()を呼び出さない理由
ここ から取得
はい、それはすべての要素のメモリコピーを行うのは事実です。パフォーマンスの問題ですか?それはあなたのパフォーマンス要件に依存します。
List
には、すべての要素を保持するための配列が内部に含まれています。容量がリストに対して十分でなくなると、配列は大きくなります。その場合はいつでも、リストはすべての要素を新しい配列にコピーします。これは常に発生し、ほとんどの人にとってパフォーマンスの問題はありません。
例えば。デフォルトのコンストラクターを持つリストは容量16から始まり、17番目の要素を.Add()
すると、サイズ32の新しい配列が作成され、16個の古い値がコピーされ、17番目が追加されます。
サイズの違いは、ToArray()
がプライベート参照を渡す代わりに新しい配列インスタンスを返す理由でもあります。
配列に新しい参照を作成しますが、そのメソッドで実行できることと実行する必要があるのはそれだけです...
パフォーマンスは相対的な用語で理解する必要があります。配列をリストに変換するには、配列をコピーする必要があり、そのコストは配列のサイズによって異なります。しかし、そのコストをプログラムが実行している他のことと比較する必要があります。そもそも、アレイに入れる情報をどのようにして入手したのですか?ディスク、ネットワーク接続、またはデータベースからの読み取りによるものである場合、メモリ内のアレイコピーが、所要時間に検出可能な違いをもたらす可能性はほとんどありません。
これは、Microsoftの 公式ドキュメント がList.ToArrayの時間計算量について述べていることです。
要素は、Array.Copyを使用してコピーされます。これはO(n)操作です。ここで、nはCountです。
次に、 Array.Copyを見て 、通常はデータのクローンを作成するのではなく、参照を使用していることがわかります。
SourceArrayとdestinationArrayが両方とも参照型の配列であるか、両方ともObject型の配列である場合、シャローコピーが実行されます。配列の浅いコピーは、元の配列と同じ要素への参照を含む新しい配列です。要素自体または要素によって参照されるものはコピーされません。対照的に、配列のディープコピーは、要素と、要素によって直接または間接的に参照されるすべてのものをコピーします。
したがって、結論として、これはリストから配列を取得する非常に効率的な方法です。
長さがわかっているあらゆる種類のList/ICollectionの場合、最初から正確に正しいサイズの配列を割り当てることができます。
T[] destinationArray = new T[this._size];
Array.Copy(this._items, 0, destinationArray, 0, this._size);
return destinationArray;
ソースタイプがIEnumerable(リスト/コレクションではない)の場合、ソースは次のとおりです。
items = new TElement[4];
..
if (no more space) {
TElement[] newItems = new TElement[checked(count * 2)];
Array.Copy(items, 0, newItems, 0, count);
items = newItems;
サイズ4から始まり、指数関数的に増加し、スペースがなくなるたびに2倍になります。倍増するたびに、メモリを再割り当てしてデータをコピーする必要があります。
ソースデータのサイズがわかっていれば、このわずかなオーバーヘッドを回避できます。ただし、ほとんどの場合、たとえば配列サイズ<= 1024の場合、実行速度が非常に速いため、この実装の詳細について考える必要はありません。
参照:Enumerable.cs、List.cs(F12ing into them)、Joeの回答