向数组添加元素的最快方法

29

如何快速将新项添加到现有数组中?

Dim arr As Integer() = {1, 2, 3}
Dim newItem As Integer = 4

我已经知道,当处理动态项目列表时,应该使用ListArrayList或类似的IEnumerables。但如果你被困在使用数组的旧代码中怎么办?

到目前为止,我尝试过:

' A) converting to List, add item and convert back
Dim list As List(Of Integer)(arr)
list.Add(newItem)
arr = list.ToArray()
' --> duration for adding 100.000 items: 33270 msec

' B) redim array and add item
ReDim Preserve arr(arr.Length)
arr(arr.Length - 1) = newItem
' --> duration for adding 100.000 items: 9237 msec

' C) using Array.Resize
Array.Resize(arr, arr.Length + 1)
arr(arr.Length - 1) = newItem
' --> duration for adding 100.000 items: 1 msec
' --> duration for adding 100.000.000 items: 1168 msec

A)因为每次添加一个项目都要进行整个数组的两次转换,所以速度非常慢。B)似乎更快了,但仍然在ReDim Preserve期间复制了一次数组。C)目前看来似乎是最快的。还有更好的方法吗?


恕我直言,我认为你在比较苹果和芒果:没有人会使用你的第一个选择。列表的优点之一是您可以快速将新项目添加到其中(如果您不进行转换以形成数组,只需添加项目,您会发现它比任何其他替代方案都要快得多):如果您只想快速添加项目,请使用列表(根本不依赖于数组)。此外,列表允许检查/索引其项目的选项比数组允许的要多得多。但除此之外,在纯性能方面(例如在循环中),它们要差得多... - varocarbas
4
摘要:在最佳情况下使用数组和列表。尽管VB.NET允许重新调整大小,但这不是预期通过数组传递的内容:在固定大小的条件下,数组在其元素内部反复迭代,提供最佳性能。另一方面,列表用于较少迭代处理:元素数量较少,尺寸经常变化,访问元素的花式查询等等,这些都是数组不擅长的功能。因此,数组用于固定大小条件下的性能;列表用于变化条件。 - varocarbas
1
PS:列表的内存效率也较低。 - varocarbas
感谢您的评论。构建这样的测试的原因是我想拥有一个将项目添加到数组的“扩展”方法。这样编码需要的行数就更少了 :-) - jor
好吧...如果在您特定的条件下它为您提供了最佳结果,请这样做;但是,从理论上讲,这没有任何意义:您正在将列表的定义性快速添加功能转换为真正缓慢的添加。另一方面,少一行代码确实会让您感到高兴 ;) - varocarbas
我真的认为你应该只使用列表。 - user4951
5个回答

43
案例C)是最快的。将其作为扩展程序:
Public Module MyExtensions
    <Extension()> _
    Public Sub Add(Of T)(ByRef arr As T(), item As T)
        Array.Resize(arr, arr.Length + 1)
        arr(arr.Length - 1) = item
    End Sub
End Module

使用方法:

Dim arr As Integer() = {1, 2, 3}
Dim newItem As Integer = 4
arr.Add(newItem)

' --> duration for adding 100.000 items: 1 msec
' --> duration for adding 100.000.000 items: 1168 msec

我尝试了,应该是 Array.Resize(arr, arr.Length + 1)。 - user4951
对于那些不知道下一步该怎么做的人,只需添加新的模块文件,并在下面放置@jor代码(带有我的小修改,支持“nothing”数组)。 +---- 编辑,代码混乱。请查看我下面的答案。 - Eric F.
2
我是一名长期使用Python的用户,正在尝试使用VB进行概念验证项目。当我发现VB不支持动态添加数组的'.add()'方法时,我偶然发现了你的解决方案。但是,当我将你的代码放入模块中时,Visual Studio会在<extension()>下划线,并报告“System.Runtime.CompilerServices.ExtensionAttribute在此上下文中不可访问,因为它是'Friend'”。我感到困惑,因为子程序是公共的,模块也是公共的。你有什么想法吗? - Aaron C. de Bruyn
2
将“Imports System.Runtime.CompilerServices”添加到代码中以使用扩展。 - Samuel Surya
请记住,ExtensionAttribute 仅在 .NET Framework 3.5+ 中可用。 - Sunny Patel

13
Dim arr As Integer() = {1, 2, 3}
Dim newItem As Integer = 4
ReDim Preserve arr (3)
arr(3)=newItem

了解更多信息请查看Redim


5
从技术角度讲,你应该写成:ReDim Preserve arr(3),否则你会使数组维数超过 1。个人而言,我并不太在意这个问题(我几乎每次都会将数组维数超过 1),但我认为这会是一个更加恰当的回答。 - varocarbas
数组已经初始化为3个项目,他们想再添加一个项目,所以大小应该是4,对吧? - Massimiliano Peluso
1
是的,但是ReDim Preserve arr(4)并不表示大小为4,而是最高索引为4,也就是大小为5。正如所说,我在这方面不应该给出任何教训,因为我总是做你所做的事情,但从理论上讲,这是不正确的。 - varocarbas
1
你说得对。我已经修正了我的答案。这就是掌握太多编程语言的坏处 :-) - Massimiliano Peluso
没问题。这是我们所有人都会犯的典型错误。C#和VB有很多共同点,这种细节可能很容易被忽视。 - varocarbas
我在模块前添加了“Imports System.Runtime.CompilerServices”以使其识别扩展。 - Robert Cody

9

对于那些不知道该怎么做的人,只需要添加新的模块文件,并将带有我的小小修改(支持“nothing”数组)的@jor代码放在下面即可。

Module ArrayExtension
    <Extension()> _
    Public Sub Add(Of T)(ByRef arr As T(), item As T)
        If arr IsNot Nothing Then
            Array.Resize(arr, arr.Length + 1)
            arr(arr.Length - 1) = item
        Else
            ReDim arr(0)
            arr(0) = item
        End If

    End Sub
End Module

7
不太干净,但能用 :)
Dim arr As Integer() = {1, 2, 3}
Dim newItem As Integer = 4

arr = arr.Concat({newItem}).ToArray

0

这取决于您插入或读取的频率。如果需要,您可以增加数组超过一个。

numberOfItems = ??

' ...

If numberOfItems+1 >= arr.Length Then
    Array.Resize(arr, arr.Length + 10)
End If

arr(numberOfItems) = newItem
numberOfItems += 1

另外对于A,只有在需要时才需要获取数组。

Dim list As List(Of Integer)(arr) ' Do this only once, keep a reference to the list
                                  ' If you create a new List everything you add an item then this will never be fast

'...

list.Add(newItem)
arrayWasModified = True

' ...

Function GetArray()

    If arrayWasModified Then
        arr = list.ToArray()
    End If

    Return Arr
End Function

如果您有时间的话,我建议您将所有内容转换为列表并删除数组。
* 我的代码可能无法编译。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接