我经常遇到这样的情况:我想在声明查询的地方对查询进行求值。这通常是因为我需要对它进行多次迭代,计算成本很高。例如:
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() -在知道内存不会被分配两次的情况下安全可靠?
我同意@mquander的观点,性能差异应该是微不足道的。但是,我想对它进行基准测试,所以我这样做了——结果是微不足道的。
Testing with List<T> source:
ToArray time: 1934 ms (0.01934 ms/call), memory used: 4021 bytes/array
ToList time: 1902 ms (0.01902 ms/call), memory used: 4045 bytes/List
Testing with array source:
ToArray time: 1957 ms (0.01957 ms/call), memory used: 4021 bytes/array
ToList time: 2022 ms (0.02022 ms/call), memory used: 4045 bytes/List
每个源数组/列表有1000个元素。所以你可以看到时间和记忆的差异都可以忽略不计。
我的结论是:您还可以使用ToList(),因为List<T>提供了比数组更多的功能,除非几个字节的内存确实对您很重要。
首选ToListAsync<T>()。
在实体框架6中,这两个方法最终都调用相同的内部方法,但ToArrayAsync<T>()在最后调用list.ToArray(),实现为
T[] array = new T[_size];
Array.Copy(_items, 0, array, 0, _size);
return array;
所以ToArrayAsync<T>()有一些开销,因此ToListAsync<T>()是首选。
我同意@mquander的观点,性能差异应该是微不足道的。但是,我想对它进行基准测试,所以我这样做了——结果是微不足道的。
Testing with List<T> source:
ToArray time: 1934 ms (0.01934 ms/call), memory used: 4021 bytes/array
ToList time: 1902 ms (0.01902 ms/call), memory used: 4045 bytes/List
Testing with array source:
ToArray time: 1957 ms (0.01957 ms/call), memory used: 4021 bytes/array
ToList time: 2022 ms (0.02022 ms/call), memory used: 4045 bytes/List
每个源数组/列表有1000个元素。所以你可以看到时间和记忆的差异都可以忽略不计。
我的结论是:您还可以使用ToList(),因为List<T>提供了比数组更多的功能,除非几个字节的内存确实对您很重要。
一个很晚的答案,但我认为这对谷歌人有帮助。
They both suck when they created using linq. They both implement same code to resize buffer if necessary. ToArray internally uses a class to convert IEnumerable<> to array, by allocating an array of 4 elements. If that is not enough than it doubles the size by creating a new array double the size of current and copying current array to it. At the end it allocates a new array of count of your items. If your query returns 129 elements then ToArray will make 6 allocations and memory copy operations to create a 256 element array and than am another array of 129 to return. so much for memory efficiency.
ToList做同样的事情,但是它跳过了最后的分配,因为您可以在将来添加项。List不关心它是从linq查询创建的还是手动创建的。
List在内存上更好,但在cpu上更差,因为List是一个通用的解决方案,每个操作都需要范围检查,除了.net内部的数组范围检查之外。
因此,如果你将迭代你的结果集太多次,那么数组是很好的,因为它意味着比列表更少的范围检查,编译器通常优化数组的顺序访问。
如果在创建List时指定capacity参数,则它的初始化分配可以更好。在这种情况下,它将只分配数组一次,假设您知道结果大小。linq的ToList没有指定重载来提供它,因此我们必须创建扩展方法,该方法创建一个具有给定容量的列表,然后使用list <>. addrange。
为了完成这个问题,我必须写出下面的句子
At the end, you can use either an ToArray, or ToList, performance will not be so different ( see answer of @EMP ).
You are using C#. If you need performance then do not worry about writing about high performance code, but worry about not writing bad performance code.
Always target x64 for high performance code. AFAIK, x64 JIT is based on C++ compiler, and does some funny things like tail recursion optimizations.
With 4.5 you can also enjoy the profile guided optimization and multi core JIT.
At last, you can use async/await pattern to process it quicker.