Cod*_*ody 131 c# arrays performance list
使用时ToList()
,是否需要考虑性能影响?
我正在编写一个查询来从目录中检索文件,这是查询:
string[] imageArray = Directory.GetFiles(directory);
但是,由于我喜欢与之合作List<>
,我决定投入......
List<string> imageList = Directory.GetFiles(directory).ToList();
那么,在决定进行这样的转换时是否应该考虑某种性能影响 - 或者仅在处理大量文件时考虑?这是一个微不足道的转换?
Dan*_*mms 167
IEnumerable.ToList()
是的,IEnumerable<T>.ToList()
确实会对性能产生影响,它是O(n)操作,尽管它可能只需要在性能关键操作中需要注意.
该ToList()
操作将使用List(IEnumerable<T> collection)
构造函数.这个构造函数必须复制数组(更一般地说IEnumerable<T>
),否则原始数组的未来修改也会在源上发生变化,T[]
这通常是不可取的.
我想重申一下这只会对一个庞大的列表产生影响,复制内存块是一个相当快速的操作.
As
vsTo
您会注意到在LINQ中有几种方法以As
(例如AsEnumerable()
)和To
(例如ToList()
)开头.开始的方法To
需要像上面那样的转换(即可能影响性能),并且开头的方法As
不需要,只需要一些强制转换或简单的操作.
List<T>
这里有一些关于如何List<T>
工作的细节,如果你感兴趣的话:)
A List<T>
还使用称为动态数组的构造,需要根据需要调整大小,此resize事件将旧数组的内容复制到新数组.所以它从小开始,如果需要增加尺寸.
这是Capacity
和Count
属性之间的区别List<T>
.Capacity
指幕后阵列的大小,是总是Count
在List<T>
哪个项目的数量<= Capacity
.因此,当一个项目被添加到列表中时,将其增加一倍Capacity
,其大小将List<T>
加倍并复制该数组.
Che*_*hen 35
调用toList()时是否会影响性能?
当然是.理论上甚至i++
会对性能产生影响,它可能会使程序减慢几个小时.
怎么.ToList
办?
当您调用时.ToList
,代码调用Enumerable.ToList()
哪个是扩展方法return new List<TSource>(source)
.在相应的构造函数中,在最糟糕的情况下,它遍历项容器并将它们逐个添加到新容器中.所以它的行为对性能的影响很小.成为应用程序的性能瓶颈是不可能的.
问题中的代码有什么问题
Directory.GetFiles
经过文件夹并立即将所有文件的名称返回到内存中,它有一个潜在的风险,即字符串[]会占用大量内存,从而减慢所有内容.
那应该怎么做
这取决于.如果您(以及您的业务逻辑)保证文件夹中的文件数量总是很小,则代码是可接受的.但它仍然建议使用懒惰版本:Directory.EnumerateFiles
在C#4中.这更像是一个查询,它不会立即执行,您可以在其上添加更多查询,如:
Directory.EnumerateFiles(myPath).Any(s => s.Contains("myfile"))
Run Code Online (Sandbox Code Playgroud)
一旦找到名称中包含"myfile"的文件,它将停止搜索路径.这显然有更好的表现.GetFiles
.
Mar*_*age 18
调用toList()时是否会影响性能?
就在这里.使用扩展方法Enumerable.ToList()
将从源集合构造一个新List<T>
对象,IEnumerable<T>
这当然会对性能产生影响.
但是,理解List<T>
可以帮助您确定性能影响是否显着.
List<T>
使用array(T[]
)来存储列表的元素.分配后,无法扩展数组,因此List<T>
将使用超大数组来存储列表的元素.当List<T>
增长超出基础数组的大小时,必须分配新数组,并且在列表增长之前必须将旧数组的内容复制到新的更大数组.
List<T>
从一个新的构造IEnumerable<T>
有两种情况:
源集合实现ICollection<T>
:然后ICollection<T>.Count
用于获取源集合的确切大小,并在使用源集合的所有元素复制到后备阵列之前分配匹配的后备阵列ICollection<T>.CopyTo()
.此操作非常有效,可能会映射到某些CPU指令以复制内存块.但是,就性能而言,新阵列需要内存,复制所有元素需要CPU周期.
否则,源集合的大小是未知的,并且枚举IEnumerable<T>
器用于将每个源元素一次添加到new List<T>
.最初,后备数组为空,并创建一个大小为4的数组.然后,当这个数组太小时,大小加倍,因此后备数组增长如此4,8,16,32等.每当后备数组增长时,必须重新分配它,并且必须复制到目前为止存储的所有元素.与第一种可以立即创建正确尺寸的阵列的情况相比,该操作成本更高.
此外,如果您的源集合包含33个元素,则列表最终将使用64个元素的数组浪费一些内存.
在您的情况下,源集合是一个实现的数组,ICollection<T>
因此除非您的源数组非常大,否则不应该关注性能影响.调用ToList()
将简单地复制源数组并将其包装在List<T>
对象中.对于小型收藏品来说,即使是第二种情况的表现也不值得担心.
ToList()
创建一个新列表并将元素放入其中,这意味着执行操作会产生相关成本ToList()
。如果集合较小,则不会产生非常明显的成本,但如果使用 ToList,则集合较大可能会导致性能下降。
一般来说,您不应该使用 ToList() ,除非您正在做的工作如果不将集合转换为列表就无法完成。例如,如果您只想迭代集合,则不需要执行 ToList
如果您使用 LINQ to SQL 对数据源(例如数据库)执行查询,则执行 ToList 的成本要高得多,因为当您将 ToList 与 LINQ to SQL 结合使用时,而不是执行延迟执行,即在需要时加载项目(这可能是有益的)在许多情况下)它会立即将数据库中的项目加载到内存中
它将与以下操作一样(低)效率:
var list = new List<T>(items);
Run Code Online (Sandbox Code Playgroud)
如果你反汇编带有 的构造函数的源代码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
以制作每个元素的副本。
“是否需要考虑性能影响?”
精确方案的问题在于,首先,您真正对性能的真正关注将来自硬盘的速度和驱动器缓存的效率。
从这个角度来看,影响是可以忽略不计肯定该点NO就不必考虑。
但是只有在您确实需要List<>
结构的功能时,才能使您的工作效率更高,算法更友好或具有其他优势。否则,您只是故意添加了微不足道的性能影响,根本没有任何原因。在这种情况下,自然不应该这样做!:)