クエリを宣言した場所で評価したいというケースによく遭遇します。これは通常、クエリを複数回反復する必要があり、計算コストが高いためです。たとえば、次のようになります。
string raw = "...";
var lines = (from l in raw.Split('\n')
let ll = l.Trim()
where !string.IsNullOrEmpty(ll)
select ll).ToList();
これは問題なく動作します。ただし、結果を変更しない場合は、ToArray()
の代わりに を呼び出す方がよいでしょうToList()
。
ToArray()
しかし、は最初に を呼び出すことによって実装されるためToList()
、 を呼び出すだけの場合よりもメモリ効率が低くなるのではないかと思いますToList()
。
私がおかしいのでしょうか?ToArray()
メモリが 2 回割り当てられないという安心感を持って、ただ呼び出すだけでいいのでしょうか?
ベストアンサー1
他の制約を満たすために配列が必要なだけの場合を除いて、 を使用する必要がありますToList
。ほとんどのシナリオでは、ToArray
よりも多くのメモリが割り当てられますToList
。
どちらもストレージに配列を使用しますが、ToList
より柔軟な制約があります。配列は少なくともコレクション内の要素数と同じ大きさである必要があります。配列が大きくても問題はありません。ただし、ToArray
配列のサイズは要素数と正確に一致する必要があります。
この制約を満たすために、ToArray
は よりも 1 つ多くの割り当てを行うことがよくありますToList
。十分な大きさの配列を取得すると、正確なサイズの配列を割り当て、その配列に要素をコピーし直します。これを回避できるのは、配列の拡張アルゴリズムが、格納する必要のある要素の数と偶然一致する場合のみです (間違いなく少数派です)。
編集
値に余分な未使用メモリが含まれることによる結果について、何人かの人から質問を受けましたList<T>
。
これは正当な懸念事項です。作成されたコレクションが長期間存続し、作成後に変更されることがなく、Gen2 ヒープに格納される可能性が高い場合は、事前に追加の割り当てを行う方がよい場合がありますToArray
。
ToArray
しかし、一般的には、これはまれなケースだと思います。メモリのその他の短期間の使用にすぐに渡される呼び出しが多数ある場合の方がはるかに一般的であり、その場合のToList
方が明らかに優れています。
ここで重要なのは、プロファイリング、プロファイリング、そしてさらにプロファイリングを行うことです。