首先,我不想使用嵌套的For循环,因为我读到它们会随着数据量的增加而变慢(n可以达到10k)。因此,现在,我有了一个数组arrData(n,2)
,而n
是可变的,我想使用for每条语句逐行遍历此数组。
这是我的代码。为简化起见,我插入了(2,2)数组:
Sub test()
Dim arrData(2, 2) As Variant
arrData(0, 0) = 0
arrData(0, 1) = 0
arrData(0, 2) = 0
arrData(1, 0) = 1
arrData(1, 1) = 1
arrData(1, 2) = 1
arrData(2, 0) = 2
arrData(2, 1) = 2
arrData(2, 2) = 2
For Each Element In arrData
MsgBox Element
Next Element
End Sub
我得到012012012,但我想得到000111222。
答案 0 :(得分:4)
首先,让我们解决这个误解:
我读到随着数据量的增加,这些速度会变慢(n最多可达到10k)
这根本不是事实。多维数组仅比一维数组“慢”,因为必须计算索引器的内存地址(稍后会详细介绍)。您最可能指的是嵌套循环的计算复杂性-迭代次数随每个循环的边界乘积而增加。您有固定数量的元素,因此无论如何 ,它们都是相同的。如果要对二维数组的每个成员执行操作o
,则将执行该计算b1 * b2次。期间。
现在,为了解释您的示例代码给出的结果,让我们看一下VBA如何在内存中布置数组。我仅通过查看数据区域(也有a SAFEARRAY structure来保存有关该数组的元信息,但实际上并不太紧密)来稍微简化一下。具有单个维度的数组被布置为内存的连续区域,并且VBA维护指向第一个元素的指针。例如,Long的一维数组将看起来像这样(Dim foo(4) As Long
):
SAFEARRAY结构包含一个指向“元素0”的指针,当您在代码中访问它时,它将索引器乘以元素类型的长度(以字节为单位),然后返回该内存地址处的值。因此,如果第一个元素位于内存地址0x0000且您访问了foo(2)
,则它将2乘以4(Long
的长度,将其添加到0x0000,然后从0x0008开始给您4个字节
基本上是A + (L * E1)
,其中A
是基址,L
是元素长度,E1
是您所请求的元素。
第二维在内存中添加N
的此布局副本,其中N
是第二维中的元素数。因此,示例代码中的数组的布局如下(Dim foo(2, 2) As Long
)
VBA与一维数组的索引相同,除了第二维之外,它将第二维的索引器与一个完整的第一维的总长度的乘积添加到一个维中元素的地址计算中。
基本上是A + (L * E1) + (L * B1 * E2)
,其中B1
是第一个维度的元素计数,E2
是第二个维度的索引。因此,如果您要从0x0000的基地址访问foo(1, 1)
,则它将为0 + (4 * 1) + (4 * 3 * 1)
,或者
0x0010。
快点说-这就是为什么除了数组的顶部维之外不能Redim Preserve
的其他原因-这是 only 情况,它是简单的内存分配和复制。>
因此,转到您的示例,您的值就这样存储在内存中:
使用For Each
时,VBA的数组迭代器仅按内存顺序将每个元素返回给 ,因此您得到012012012。针对您的特定示例,您可以通过移置它们以000111222的顺序重新获得它们-您所谓的“行”实际上是示例中的第一个维度:
Sub Example()
Dim arrData(2, 2) As Variant
arrData(0, 0) = 0
arrData(1, 0) = 0
arrData(2, 0) = 0
arrData(0, 1) = 1
arrData(1, 1) = 1
arrData(2, 1) = 1
arrData(0, 2) = 2
arrData(1, 2) = 2
arrData(2, 2) = 2
For Each Element In arrData
Debug.Print Element
Next Element
End Sub
这会像在内存中那样布置数组:
也就是说,与简单的For Each
循环相比,For
循环的开销更大,因为VBA必须使用数组枚举器并将_NewEnum
调用推入堆栈。尽管您可能会看到索引的性能有了微小的提高,因为它只是在内存地址上增加了一个偏移量,而不是每次都执行更长的计算,但与重复推入并弹出调用堆栈相比,这样做的好处不胜枚举。因此,长话短说,只需嵌套循环即可:
Dim outer As Long
Dim inner As Long
For outer = LBound(arrData, 1) To UBound(arrData, 1)
For inner = LBound(arrData, 2) To UBound(arrData, 2)
Debug.Print arrData(outer, inner)
Next
Next
在您的情况下,您将通过交换内部和外部循环来“转置”数组。
注意::我没有在Excel的上下文中使用“行”(尽管它将排在第一位),通过“移置”,我的意思不是使用Excel的{{1} }函数-比任一替代方案的性能要差。
答案 1 :(得分:3)
我有点困惑。我不认为使用嵌套结构来循环行和列将证明对性能的重大影响。如果您有很多数据,那么您就有很多数据。
您需要循环行和列,以有效地访问所有项目,即嵌套循环结构。我不知道从2D数组访问每个项目的更有效方式。
您可以改进代码的地方是使用For循环,该循环比处理数组时的For Each速度快。