我知道“ redim”比“ Array.Resize”更旧,但不了解使后者更好地用于前者的原因。
最佳答案
除非添加Preserve
修饰符,否则可能无法比较ReDim和Array.Resize<T>。Array.Resize
不仅会分配一个新数组,还将源数组中的所有项目复制到目标数组。
没有ReDim
修饰符的Preserve
将仅分配一个新数组。源数组中的所有项目均丢失。
所以基本上这样:
Dim a As String() = {"item1", "item2"}
ReDim a(4 - 1) 'Double the size
'At this point, a contains 4 null references.
...等于:
Dim a As String() = {"item1", "item2"}
a = New String(4 - 1) {} 'Double the size
'At this point, a contains 4 null references.
您可以通过检查发行版配置中产生的CIL代码并查找0x8D - newarr <etype>指令来验证这一点。通过了解这一点,很明显为什么它比
Array.Resize<T>
更快。因此,让我们比较
ReDim Preserve
和Array<T>
。那么使用哪一个呢?
让我们创建两个方法并查看
CIL
代码。VB.NET
Private Sub ResizeArray1(Of T)(ByRef a As T(), size As Int32)
ReDim Preserve a(size - 1)
End Sub
Private Sub ResizeArray2(Of T)(ByRef a As T(), size As Int32)
Array.Resize(a, size)
End Sub
CIL
.method private static void ResizeArray1<T>(!!T[]& a, int32 size) cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldarg.0
L_0002: ldind.ref
L_0003: ldarg.1
L_0004: ldc.i4.1
L_0005: sub.ovf
L_0006: ldc.i4.1
L_0007: add.ovf
L_0008: newarr !!T
L_000d: call class [mscorlib]System.Array [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Utils::CopyArray(class [mscorlib]System.Array, class [mscorlib]System.Array)
L_0012: castclass !!T[]
L_0017: stind.ref
L_0018: ret
}
.method private static void ResizeArray2<T>(!!T[]& a, int32 size) cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldarg.1
L_0002: call void [mscorlib]System.Array::Resize<!!T>(!!0[]&, int32)
L_0007: ret
}
如您所见,
ReDim Preserve
最终成为对Microsoft.VisualBasic.CompilerServices.Utils.CopyArray的调用指令。现在,如果检查CopyArray
(RS上没有可用的源代码)和Array.Resize<T>,您将看到这两种方法最终都作为对Array.Copy<T>方法的CLR的调用指令。因此,可以认为它们本质上都是“相同的”,并且快速的Benckmark(在本文末尾提供)似乎可以证实这一点。
但是,正如Hans Passant正确指出的那样,无论何时需要操纵数组,都应该使用List<T>。
基准测试
迭代次数:10
最大大小:100000
空源数组:
{方法= ResizeArray1,时间= 00:00:05.6533126}
{方法= ResizeArray2,时间= 00:00:05.6973607}
不断增长的源数组:
{方法= ResizeArray1,时间= 00:01:42.6964858}
{方法= ResizeArray2,时间= 00:01:42.1891668}
Option Strict On
Public Module Program
Friend Sub Main()
Console.WriteLine("Warming up...")
Program.Benchmark(iterations:=10, maxSize:=1000, warmUp:=True)
Console.WriteLine("Warmup completed. Measurement started...")
Program.Benchmark(iterations:=10, maxSize:=100000, warmUp:=False)
Console.WriteLine()
Console.WriteLine("Measurement completed. Press any key to exit.")
Console.ReadKey()
End Sub
Private Sub Benchmark(iterations As Int32, maxSize As Int32, warmUp As Boolean)
Dim watch As Stopwatch
Dim a As String()
'BY: EMPTY SOURCE ARRAY ---------------------------------
'Resize array #1
watch = Stopwatch.StartNew()
For i As Int32 = 1 To iterations
For n As Int32 = 1 To maxSize
a = Program.CreateArray(Of String)(0)
Program.ResizeArray1(a, n)
Next
Next
watch.Stop()
If (Not warmUp) Then
Console.WriteLine()
Console.WriteLine(String.Format("R E S U L T"))
Console.WriteLine()
Console.WriteLine(String.Format("Iterations: {0}", iterations))
Console.WriteLine(String.Format(" MaxSize: {0}", maxSize))
Console.WriteLine()
Console.WriteLine("Empty source array:")
Console.WriteLine()
Console.WriteLine(New With {.Method = "ResizeArray1", .Time = watch.Elapsed.ToString()})
End If
'Resize array #2
watch = Stopwatch.StartNew()
For i As Int32 = 1 To iterations
For n As Int32 = 1 To maxSize
a = CreateArray(Of String)(0)
ResizeArray2(a, n)
Next
Next
watch.Stop()
If (Not warmUp) Then
Console.WriteLine(New With {.Method = "ResizeArray2", .Time = watch.Elapsed.ToString()})
End If
'BY: GROWING SOURCE ARRAY -------------------------------
'Resize array #1
watch = Stopwatch.StartNew()
a = Program.CreateArray(Of String)(0)
For i As Int32 = 1 To iterations
For n As Int32 = 1 To maxSize
Program.ResizeArray1(a, n)
Next
Next
watch.Stop()
If (Not warmUp) Then
Console.WriteLine()
Console.WriteLine("Growing source array:")
Console.WriteLine()
Console.WriteLine(New With {.Method = "ResizeArray1", .Time = watch.Elapsed.ToString()})
End If
'Resize array #2
watch = Stopwatch.StartNew()
a = Program.CreateArray(Of String)(0)
For i As Int32 = 1 To iterations
For n As Int32 = 1 To maxSize
Program.ResizeArray2(a, n)
Next
Next
watch.Stop()
If (Not warmUp) Then
Console.WriteLine(New With {.Method = "ResizeArray2", .Time = watch.Elapsed.ToString()})
End If
End Sub
Private Function CreateArray(Of T)(size As Int32) As T()
Return New T(size - 1) {}
End Function
Private Sub ResizeArray1(Of T)(ByRef a As T(), size As Int32)
ReDim Preserve a(size - 1)
End Sub
Private Sub ResizeArray2(Of T)(ByRef a As T(), size As Int32)
Array.Resize(a, size)
End Sub
End Module