以下示例中的List1是SortedList(Of MyClass),包含251个成员。
前两个代码块在15.5秒内执行。
For cnt As Integer = 1 To 1000000
For Each TempDE In List1
Dim F As String = TempDE.Key
TempDE.Value.x1 = 444
Next
Next
For cnt As Integer = 1 To 1000000
For Each TempDE As KeyValuePair(Of String, phatob) In List2
Dim F As String = TempDE.Key
TempDE.Value.x1 = 444
Next
Next
这个在5.6秒内执行。
For cnt As Integer = 0 To 999999
For cnt2 As Integer = 0 To 250
Dim F As String = List1.Keys(cnt2)
List1.Values(cnt2).x1 = 444
Next
Next
为什么前两个代码块这么慢?
答案 0 :(得分:4)
SortedList通过实现IComparer来扩展Collection,以提供排序功能。在内部,它实现了2个数组来存储列表的元素 - 一个用于键的数组,另一个用于值。 .NET阵列针对快速有序和快速随机访问进行了优化。
我怀疑前两个是慢的原因是因为SortedList中的foreach语句是Enumerator的包装器。调用foreach将查询枚举器,调用MoveNext和Current。此外,通过通用列表可能会在遍历列表时涉及装箱和拆箱,并且可能会产生通过索引访问通常无法获得的性能开销。
答案 1 :(得分:3)
我试图查看有关For Each
行为的确切文档,但我找不到它。
我的理论是,使用For Each
语句将列表中的对象复制到内存中的另一个位置,然后在循环的每次迭代结束时将其复制回列表中。
另一种可能性是它在每次迭代开始时调用构造函数然后解构并再次调用构造函数以重置以进行下一次迭代。
我对这两种理论都不确定,但3和(1或2)之间的主要区别在于缺少For Each
。
编辑:在MSDN找到了一些文档。
以下是摘录:
当开始执行For Each ... Next循环时,Visual Basic会验证该组是否指向有效的集合对象。如果没有,它会抛出异常。否则,它调用MoveNext方法和枚举器对象的Current属性来返回第一个元素。如果MoveNext指示没有下一个元素,也就是说,如果集合为空,则For Each循环终止并且控制传递给Next语句之后的语句。否则,Visual Basic将元素设置为第一个元素并运行语句块。
总的来说,听起来For Each
更像是“被管理”,并且需要花费很多开销才能确保所有内容都匹配。结果,它变慢了。
答案 2 :(得分:3)
我认为由于固定的循环范围,编译器可以更好地优化块3。在第一和第二块中,编译器将不知道循环的上限是什么,直到它评估List,从而使它变慢。
答案 3 :(得分:2)
我随机猜测: List1包含~750个元素(而不仅仅是250个)。 第三种情况更快,因为它不会遍历List1所拥有的每个元素。