我有一个IEnumerable(Of Integer)
,想要将每组三个连续的整数相加,从第一个开始,到最后一个减去两个。
示例:我的整数是{0, 6, 2, 8, 2, 0, 3, 7, 1}
。我想将其转换为{0+60+2, 600+20+8, 200+80+2, ...} = {62, 628, 282, 820, 203, 37, 371}
,然后将其存储在有序集中。请注意,我选择了基数10来简化问题,实际上基数是变化的(设置的最大值加1,因此我的方法会为集合中三个数字的每个排列生成一个唯一的ID)。
我测试了三段不同的代码:
1)
sums = New SortedSet(Of Integer)()
For i = 0 To integers.Count - 3
sums.Add(integers(i) * 100 + integers(i + 1) * 10 + integers(i + 2))
Next
2)
sums = New SortedSet(Of Integer)(
integers.Take(integers.Count - 2).Select(
Function(val, index) val * 100 + integers(index + 1) * 10 + integers(index + 2)
)
)
3)
Dim num = integers.Count - 2
Dim intsX = integers.Take(num)
Dim intsY = integers.Skip(1).Take(num)
Dim intsZ = integers.Skip(2)
sums = New SortedSet(Of Integer)(
intsX.Zip(
intsY, Function(x, y) x * 100 + y * 10).Zip(
intsZ, Function(xy, z) xy + z)
)
所有三种方法都有效,但我的大约62k整数集的测试表明执行时间存在显着差异:方法1):1.8-2秒,方法2:1.4-1.5秒,方法3:0.75-0.77秒
由于性能对我的项目至关重要,我仍然不确定这是否是实现我想要的最佳代码。有没有人有另外一个我可以测试的想法?
答案 0 :(得分:1)
第一次和第二次尝试的问题都与VB.NET在IEnumerable(Of T)
上解决类似数组索引器访问的方式有关。这种类型的调用被转换为ElementAtOrDefault()
扩展方法调用,因此您的第一个方法看起来像这样:
For i = 0 To integers.Count - 3
sums.Add(integers.ElementAtOrDefault(i) * 100 + integers.ElementAtOrDefault(i + 1) * 10 + integers.ElementAtOrDefault(i + 2))
Next
是的,ElementAtOrDefault()
每次调用时都需要从集合的开头迭代(取决于实际的IEnumerable(Of T)
实现)。所以在更糟糕的情况下,你的解决方案一遍又一遍地迭代你的收藏,你甚至都不知道。
这也是Zip
让它变得更好的原因。它只对你的源集合进行3次迭代,这比之前描述的情况要好得多。
另一种可能的解决方案 :
您可以尝试Aggregate
:
Dim sums = New SortedSet(Of Integer)()
source.Aggregate(
New List(Of Integer)(3),
Function(l As List(Of Integer), i As Integer)
l.Add(i)
If l.Count = 3 Then
sums.Add(l(0) * 100 + l(1) * 10 + l(2))
l.RemoveAt(0)
End If
Return l
End Function,
Function(l) l)
是的,我使用了索引器访问语法,但因为它位于帮助器List(Of T)
上,所以它实际上是一个索引器访问,而不是隐藏的IEnumerable.ElementAtOrDefault(index)
调用。