Visual Basic 中的数组

数组是逻辑上彼此相关的一组值(称为元素)。 例如,某个数组包含文法学校每个年级的学生人数;该数组的每个元素是一个年级的学生人数。 类似地,某个数组包含某个学生的班级成绩等级;该数组的每个元素是一个成绩等级。

可以使用单个变量来存储每个数据项。 例如,如果我们的应用程序分析学生的成绩等级,则我们可对每个学生的成绩等级使用单独的变量,例如 englishGrade1englishGrade2 等。这种方法存在三个主要限制:

  • 在设计时必须确切地知道我们必须处理多少个成绩等级。
  • 很难快速处理大量的成绩等级。 而这又会使应用程序更有可能出现严重的 bug。
  • 维护很困难。 添加每个新的成绩等级都需要修改、重新编译再重新部署应用程序。

如果使用数组,可以按相同的名称引用这些相关值,并使用一个称作“索引”或“下标”的编号根据单个元素在数组中的位置来标识该元素。 数组的索引范围从 0 开始,到数组中的元素总数减 1。 使用 Visual Basic 语法定义数组的大小时,需指定它的最高索引,而不是数组中的元素总数。 可将数组用作单元,并可以迭代其元素,因而在设计时无需确切知道该数组包含多少元素。

在进行说明之前,请看几个简单的示例:

' Declare a single-dimension array of 5 numbers.
Dim numbers(4) As Integer

' Declare a single-dimension array and set its 4 values.
Dim numbers = New Integer() {1, 2, 4, 8}

' Change the size of an existing array to 16 elements and retain the current values.
ReDim Preserve numbers(15)

' Redefine the size of an existing array and reset the values.
ReDim numbers(15)

' Declare a 6 x 6 multidimensional array.
Dim matrix(5, 5) As Double

' Declare a 4 x 3 multidimensional array and set array element values.
Dim matrix = New Integer(,) {{1, 2, 3}, {2, 3, 4}, {3, 4, 5}, {4, 5, 6}}

' Declare a jagged array
Dim sales()() As Double = New Double(11)() {}

简单数组中的数组元素

让我们创建一个名为 students 的数组来存储文法学校每个年级的学生人数。 这些元素的索引范围为 0 到 6。 使用此数组比声明 7 个变量更方便。

下图显示了 students 数组。 对于数组的每个元素:

  • 元素索引表示年级(索引 0 表示幼儿园)。

  • 元素中包含的值表示每个年级的学生数量。

此图显示学生人数的数组

以下示例包含用于创建和使用数组的 Visual Basic 代码:


Module SimpleArray
   Public Sub Main()
      ' Declare an array with 7 elements.
      Dim students(6) As Integer

      ' Assign values to each element.
      students(0) = 23
      students(1) = 19
      students(2) = 21
      students(3) = 17
      students(4) = 19
      students(5) = 20
      students(6) = 22
      
      ' Display the value of each element.
      For ctr As Integer = 0 To 6
         Dim grade As String = If(ctr = 0, "kindergarten", $"grade {ctr}")
         Console.WriteLine($"Students in {grade}: {students(ctr)}")
      Next
   End Sub
End Module
' The example displays the following output:
'     Students in kindergarten: 23
'     Students in grade 1: 19
'     Students in grade 2: 21
'     Students in grade 3: 17
'     Students in grade 4: 19
'     Students in grade 5: 20
'     Students in grade 6: 22

该示例执行三项操作:

  • 声明包含 7 个元素的 students 数组。 数组声明中的编号 6 表示该数组中的最后一个索引;它是数组中的元素数量减 1。
  • 将值赋给数组中的每个元素。 可以使用数组名称并在括号中包含单个元素的索引,来访问数组元素。
  • 列出数组的每个值。 该示例使用 For 语句按索引编号访问数组的每个元素。

以上示例中的 students 数组是一维数组,因为它使用一个索引。 使用多个索引或下标的数组则称为多维数组。 有关详细信息,请参阅本文的余下部分和 Visual Basic 中的数组维度

创建数组

可通过多种方式定义数组的大小:

  • 可以在声明数组时指定大小:

    ' Declare an array with 10 elements.
    Dim cargoWeights(9) As Double               
    ' Declare a 24 x 2 array.
    Dim hourlyTemperatures(23, 1) As Integer
    ' Declare a jagged array with 31 elements.
    Dim januaryInquiries(30)() As String
    
  • 可以在创建数组时使用 New 子句提供数组的大小:

    ' Declare an array with 10 elements.
    Dim cargoWeights() As Double = New Double(9) {}
    ' Declare a 24 x 2 array.
    Dim hourlyTemperatures(,) As Integer = New Integer(23, 1) {}
    ' Declare a jagged array with 31 elements. 
    Dim januaryInquiries()() As String = New String(30)() {}
    

如果你具有现有数组,你可以通过使用 ReDim 语句来重新定义其大小。 可以指定 ReDim 语句应保留数组中的值,或者可以指定它应该创建一个空数组。 下面的示例演示了用 ReDim 语句来修改现有数组的大小的不同用法。

' Assign a new array size and retain the current values.
ReDim Preserve cargoWeights(20)
' Assign a new array size and retain only the first five values.
ReDim Preserve cargoWeights(4)
' Assign a new array size and discard all current element values.
ReDim cargoWeights(15)

有关详细信息,请参阅 ReDim 语句

存储数组中的值

你可以通过使用类型 Integer的索引访问数组中的每个位置。 你可以使用括号内的索引来引用每个数组位置,从而存储和检索数组中的值。 多维数组的索引以逗号 (,) 分隔。 每个数组维度都需要一个索引。

以下示例演示了用于存储和检索数组中的值的一些语句。


Module Example
   Public Sub Main()
      ' Create a 10-element integer array.
      Dim numbers(9) As Integer
      Dim value As Integer = 2
        
      ' Write values to it.
      For ctr As Integer = 0 To 9
         numbers(ctr) = value
         value *= 2
      Next
        
      ' Read and sum the array values.  
      Dim sum As Integer
      For ctr As Integer = 0 To 9
         sum += numbers(ctr)
      Next
      Console.WriteLine($"The sum of the values is {sum:N0}")
    End Sub
End Module
' The example displays the following output:
'     The sum of the values is 2,046

使用数组文本填充数组

使用数组文本,可以在创建数组的同时使用一组初始值填充该数组。 数组文本包含用逗号分隔的值列表,这些值被括在括号内 ({})。

通过使用数组文本创建数组时,可以提供数组类型或使用类型推理功能来确定数组类型。 以下示例演示了这两个选项。

' Array literals with explicit type definition.
Dim numbers = New Integer() {1, 2, 4, 8}
' Array literals with type inference.
Dim doubles = {1.5, 2, 9.9, 18}
' Array literals with explicit type definition.
Dim articles() As String = { "the", "a", "an" }

' Array literals with explicit widening type definition.
Dim values() As Double = { 1, 2, 3, 4, 5 }

使用类型推理时,数组的类型由文字值列表中的主控类型确定。 主控类型是数组中的所有其他类型可以扩大到的类型。 如果无法确定此唯一类型,基准类型是数组中所有其他类型可以缩小到的唯一类型。 如果无法确定为这两种唯一类型之一,则基准类型是 Object。 例如,如果提供给数组文本的值的列表包含 IntegerLongDouble类型的值,则生成的数组类型是 Double。 由于 IntegerLong 只能扩大到 Double,因此 Double 是主控类型。 有关详细信息,请参阅 Widening and Narrowing Conversions

注意

只能对在类型成员中定义为局部变量的数组使用类型推理。 如果不存在显式类型定义,则在类级别使用数组文本定义的数组为 Object[] 类型。 有关详细信息,请参阅局部类型推理

请注意,尽管所有数组文本的类型为 Integer,但以上示例将 values 定义为 Double 类型的数组。 之所以能够创建此数组,是因为数组文字中的值可以扩大为 Double 值。

还可以使用嵌套数组文本来创建和填充多维数组。 嵌套数组文本必须具有与生成的数组一致的多个维度。 以下示例使用嵌套数组文本创建二维整数数组。

' Create and populate a 2 x 2 array.
Dim grid1 = {{1, 2}, {3, 4}}
' Create and populate a 2 x 2 array with 3 elements.
Dim grid2(,) = {{1, 2}, {3, 4}, {5, 6}}

使用嵌套数组文本创建和填充数组时,如果嵌套数组文本中的元素数量不匹配,则会发生错误。 如果将数组变量显式声明为具有与数组文本不同的维度数,也会发生错误。

如同创建一维数组时一样,在使用嵌套数组文本创建多维数组时,可以依赖类型推理。 推理出的类型是用于所有嵌套级别的所有数组文本中的所有值的主控类型。 以下示例基于类型为 IntegerDouble 的值创建一个类型为 Double[,] 的二维数组。

Dim arr = {{1, 2.0}, {3, 4}, {5, 6}, {7, 8}}

有关其他示例,请参阅如何:在 Visual Basic 中初始化数组变量

循环访问数组

循环访问某个数组时,将从最低索引到最高索引或者从最高索引到最低索引访问数组中的每个元素。 通常使用 For...Next 语句For Each...Next 语句来循环访问数组的元素。 如果你不知道数组的上限,可以调用 Array.GetUpperBound 方法获取索引的最大值。 虽然最低索引值几乎始终为 0,但你可以调用 Array.GetLowerBound 方法来获取索引的最低值。

以下示例使用 For...Next 语句循环访问某个一维数组。


Module IterateArray
   Public Sub Main()
      Dim numbers = {10, 20, 30}

      For index = 0 To numbers.GetUpperBound(0)
         Console.WriteLine(numbers(index))
      Next
   End Sub
End Module
' The example displays the following output:
'  10
'  20
'  30

下面的示例使用 For...Next 语句循环访问一个多维数组。 GetUpperBound 方法具有用于指定维度的参数。 GetUpperBound(0) 返回第一个维度的最高索引,GetUpperBound(1) 返回第二个维度的最高索引。


Module IterateArray
   Public Sub Main()
      Dim numbers = {{1, 2}, {3, 4}, {5, 6}}

      For index0 = 0 To numbers.GetUpperBound(0)
         For index1 = 0 To numbers.GetUpperBound(1)
            Console.Write($"{numbers(index0, index1)} ")
         Next
         Console.WriteLine()
      Next
   End Sub
End Module
' The example displays the following output:
' Output 
'  1 2 
'  3 4 
'  5 6

以下示例使用 For Each...Next 语句循环访问某个一维数组和二维数组。


Module IterateWithForEach
   Public Sub Main()
      ' Declare and iterate through a one-dimensional array.
      Dim numbers1 = {10, 20, 30}
      
      For Each number In numbers1
         Console.WriteLine(number)
      Next
      Console.WriteLine()
      
      Dim numbers = {{1, 2}, {3, 4}, {5, 6}}

      For Each number In numbers
         Console.WriteLine(number)
      Next
   End Sub
End Module
' The example displays the following output:
'  10
'  20
'  30
'
'  1
'  2
'  3
'  4
'  5
'  6

数组大小

数组的大小是数组所有维度的长度的产物。 它表示数组中当前所包含的元素总数。 例如,以下示例声明一个二维数组,其中的每个维度包含四个元素。 从示例的输出中可以看到,该数组的大小为 16(即 (3 + 1) * (3 + 1))。


Module Example
   Public Sub Main()
      Dim arr(3, 3) As Integer
      Console.WriteLine(arr.Length)     
   End Sub
End Module
' The example displays the following output:
'     16

注意

此处有关数组大小的讨论不适用于交错数组。 有关交错数组和如何确定交错数组大小的信息,请参阅交错数组部分。

可以通过使用 Array.Length 属性查找数组大小。 可以使用 Array.GetLength 方法查找多维数组每个维度的长度。

可以通过为数组变量分配新的数组对象或使用 ReDim 语句来调整数组变量的大小。 以下示例使用 ReDim 语句将包含 100 个元素的数组更改为包含 51 个元素的数组。


Module Example
   Public Sub Main()
      Dim arr(99) As Integer
      Console.WriteLine(arr.Length)
      
      Redim arr(50)
      Console.WriteLine(arr.Length)
   End Sub
End Module
' The example displays the following output:
'     100
'     51

 

当处理数组大小时,需要记住几件事情。

备注
维度长度 每个维度的索引从 0 开始,这意味着它的范围为 0 到其上限。 因此,给定维度的长度比该维度的已声明上限大 1。
长度限制 数组的每个维度的长度不能超过 Integer 数据类型的最大值,即 Int32.MaxValue 或 (2 ^ 31) - 1。 但是,数组的总大小还受到系统上可用的内存限制。 如果你尝试初始化超出可用内存量的数组,运行时将引发 OutOfMemoryException
大小和元素大小 数组的大小独立于其元素的数据类型。 大小始终表示元素总数,而不是元素在内存中占用的字节数。
内存消耗 做出关于数组如何存储在内存中的假设是不可靠的。 由于不同数据宽度的平台上的存储会有所变化,因此同一数组在 64 位系统上可以占用比在 32 位系统上更多的内存。 具体取决于数组初始化时的系统配置,公共语言运行时 (CLR) 可以尽可能地将存储分配到靠近包元素的地方,或者将它们全部在自然硬件边界上对齐。 此外,数组需要存储开销的控制信息,而且每添加一个维度,这种开销随之增加。

数组类型

每个数组都有一个数据类型,该类型不同于该数组的元素的数据类型。 没有一种数据类型能用于所有数组。 相反,数组的数据类型由数组的维度数量或 “排名”,以及数组中元素的数据类型确定。 仅当两个数组变量具有相同的秩且它们的元素具有相同的数据类型时,它们才具有相同的数据类型。 数组的维度长度不会影响数组数据类型。

每个数组都继承自 System.Array 类,并且你可以声明一个类型为 Array 的变量,但不能创建一个类型为 Array 的数组。 例如,尽管以下代码将 arr 变量声明为 Array 类型并调用 Array.CreateInstance 方法来实例化数组,但该数组的类型证明为 Object[]。


Module Example
   Public Sub Main()
      Dim arr As Array = Array.CreateInstance(GetType(Object), 19)
      Console.WriteLine(arr.Length)
      Console.WriteLine(arr.GetType().Name)
   End Sub
End Module
' The example displays the following output:
'     19
'     Object[]

此外,ReDim 语句无法对声明为类型 Array 的变量执行运算。 出于这些原因和类型安全考虑,建议将每个数组声明为特定类型。

你可以通过几种方式了解到数组及其元素的数据类型。

  • 可以调用变量上的 GetType 方法来获取表示变量运行时类型的 Type 对象。 Type 对象可在其属性和方法中保存大量信息。
  • 可将该变量传递给 TypeName 函数,以获取包含运行时类型名称的 String

以下示例调用 GetType 方法和 TypeName 函数来确定数组的类型。 数组类型为 Byte(,)。 请注意,Type.BaseType 属性还指示字节数组的基类型为 Array 类。


Module Example
   Public Sub Main()
      Dim bytes(9,9) As Byte
      Console.WriteLine($"Type of {nameof(bytes)} array: {bytes.GetType().Name}")
      Console.WriteLine($"Base class of {nameof(bytes)}: {bytes.GetType().BaseType.Name}")
      Console.WriteLine()
      Console.WriteLine($"Type of {nameof(bytes)} array: {TypeName(bytes)}")
   End Sub
End Module
' The example displays the following output:
' Type of bytes array: Byte[,]
' Base class of bytes: Array
' 
' Type of bytes array: Byte(,)


作为返回值和参数的数组

若要通过 Function 过程返回数组,请将数组数据类型和维度数指定为 Function 语句的返回类型。 在函数内,声明一个具有相同数据类型和维度数的本地数组变量。 在 Return 语句中,添加不带括号的局部数组变量。

若要将作为参数的数组指定到 SubFunction 步骤中,可将此参数定义为具有指定数据类型和维度数量的数组。 在对过程的调用中,传递一个具有相同数据类型和维度数的数组变量。

在以下示例中,GetNumbers 函数返回 Integer() - 一个 Integer 类型的一维数组。 ShowNumbers 过程接受 Integer() 参数。


Module ReturnValuesAndParams
   Public Sub Main()
      Dim numbers As Integer() = GetNumbers()
      ShowNumbers(numbers)
   End Sub

   Private Function GetNumbers() As Integer()
      Dim numbers As Integer() = {10, 20, 30}
      Return numbers
   End Function

   Private Sub ShowNumbers(numbers As Integer())
      For index = 0 To numbers.GetUpperBound(0)
         Console.WriteLine($"{numbers(index)} ")
      Next
   End Sub
End Module
' The example displays the following output:
'   10
'   20
'   30
    

在以下示例中,GetNumbersMultiDim 函数返回 Integer(,) - 一个 Integer 类型的二维数组。 ShowNumbersMultiDim 过程接受 Integer(,) 参数。


Module Example
   Public Sub Main()
      Dim numbers As Integer(,) = GetNumbersMultidim()
      ShowNumbersMultidim(numbers)
   End Sub

   Private Function GetNumbersMultidim() As Integer(,)
      Dim numbers As Integer(,) = {{1, 2}, {3, 4}, {5, 6}}
      Return numbers
   End Function

   Private Sub ShowNumbersMultidim(numbers As Integer(,))
      For index0 = 0 To numbers.GetUpperBound(0)
         For index1 = 0 To numbers.GetUpperBound(1)
            Console.Write($"{numbers(index0, index1)} ")
         Next
         Console.WriteLine()
      Next
   End Sub
End Module
' The example displays the following output:
'     1 2
'     3 4
'     5 6

交错数组

有时应用程序中的数据结构是二维而不是矩形。 例如,你可能会使用一个数组来存储一个月中每天的高温数据。 该数组的第一个维度表示月份,但第二个维度表示天数,而一个月的天数并不是统一的。 在此类场景下可以使用交错数组(也称为数组的数组)。 交错数组是其元素同样为数组的数组。 交错数组和交错数组中的每个元素都可以具有一个或多个维度。

以下示例使用月份数组,其每个元素是天数的数组。 该示例使用交错数组,因为不同月份的天数不同。 该示例演示如何创建一个交错数组、为其赋值以及检索和显示其值。

Imports System.Globalization

Module JaggedArray
   Public Sub Main()
      ' Declare the jagged array of 12 elements. Each element is an array of Double.
      Dim sales(11)() As Double
      ' Set each element of the sales array to a Double array of the appropriate size.
      For month As Integer = 0 To 11
         ' The number of days in the month determines the appropriate size.
         Dim daysInMonth As Integer =
            DateTime.DaysInMonth(Year(Now), month + 1)
         sales(month) = New Double(daysInMonth - 1) {}
      Next 

      ' Store values in each element.
      For month As Integer = 0 To 11
         For dayOfMonth = 0 To sales(month).GetUpperBound(0)
            sales(month)(dayOfMonth) = (month * 100) + dayOfMonth
         Next
      Next

      ' Retrieve and display the array values.
      Dim monthNames = DateTimeFormatInfo.CurrentInfo.AbbreviatedMonthNames
      ' Display the month names.
      Console.Write("    ")
      For ctr = 0 To sales.GetUpperBound(0)
         Console.Write($" {monthNames(ctr)}   ")
      Next   
      Console.WriteLine()
      ' Display data for each day in each month.
      For dayInMonth = 0 To 30
         Console.Write($"{dayInMonth + 1,2}.  ")
         For monthNumber = 0 To sales.GetUpperBound(0)
            If dayInMonth > sales(monthNumber).GetUpperBound(0) Then 
               Console.Write("       ")
            Else
               Console.Write($"{sales(monthNumber)(dayInMonth),-5}  ")
            End If
         Next   
         Console.WriteLine()
      Next
   End Sub
End Module
' The example displays the following output:
'      Jan    Feb    Mar    Apr    May    Jun    Jul    Aug    Sep    Oct    Nov    Dec
'  1.  0      100    200    300    400    500    600    700    800    900    1000   1100
'  2.  1      101    201    301    401    501    601    701    801    901    1001   1101
'  3.  2      102    202    302    402    502    602    702    802    902    1002   1102
'  4.  3      103    203    303    403    503    603    703    803    903    1003   1103
'  5.  4      104    204    304    404    504    604    704    804    904    1004   1104
'  6.  5      105    205    305    405    505    605    705    805    905    1005   1105
'  7.  6      106    206    306    406    506    606    706    806    906    1006   1106
'  8.  7      107    207    307    407    507    607    707    807    907    1007   1107
'  9.  8      108    208    308    408    508    608    708    808    908    1008   1108
' 10.  9      109    209    309    409    509    609    709    809    909    1009   1109
' 11.  10     110    210    310    410    510    610    710    810    910    1010   1110
' 12.  11     111    211    311    411    511    611    711    811    911    1011   1111
' 13.  12     112    212    312    412    512    612    712    812    912    1012   1112
' 14.  13     113    213    313    413    513    613    713    813    913    1013   1113
' 15.  14     114    214    314    414    514    614    714    814    914    1014   1114
' 16.  15     115    215    315    415    515    615    715    815    915    1015   1115
' 17.  16     116    216    316    416    516    616    716    816    916    1016   1116
' 18.  17     117    217    317    417    517    617    717    817    917    1017   1117
' 19.  18     118    218    318    418    518    618    718    818    918    1018   1118
' 20.  19     119    219    319    419    519    619    719    819    919    1019   1119
' 21.  20     120    220    320    420    520    620    720    820    920    1020   1120
' 22.  21     121    221    321    421    521    621    721    821    921    1021   1121
' 23.  22     122    222    322    422    522    622    722    822    922    1022   1122
' 24.  23     123    223    323    423    523    623    723    823    923    1023   1123
' 25.  24     124    224    324    424    524    624    724    824    924    1024   1124
' 26.  25     125    225    325    425    525    625    725    825    925    1025   1125
' 27.  26     126    226    326    426    526    626    726    826    926    1026   1126
' 28.  27     127    227    327    427    527    627    727    827    927    1027   1127
' 29.  28            228    328    428    528    628    728    828    928    1028   1128
' 30.  29            229    329    429    529    629    729    829    929    1029   1129
' 31.  30            230           430           630    730           930           1130

以上示例使用 For...Next 循环按照每个元素为交错数组赋值。 也可以使用嵌套数组文本为交错数组的元素赋值。 但是,尝试使用嵌套数组文本(例如 Dim valuesjagged = {{1, 2}, {2, 3, 4}})会生成编译器错误 BC30568。 若要更正该错误,请将内部数组文本括在括号中。 括号强制计算数组文本表达式,并将生成的值与外部数组文本一起使用,如以下示例所示。


Module Example
   Public Sub Main()
      Dim values1d = { 1, 2, 3 }
      Dim values2d = {{1, 2}, {2, 3}, {3, 4}}
      Dim valuesjagged = {({1, 2}), ({2, 3, 4})}
   End Sub
End Module

交错数组是其元素包含数组的一维数组。 因此,Array.Length 属性和 Array.GetLength(0) 方法返回一维数组中的元素数,而 Array.GetLength(1) 引发 IndexOutOfRangeException,因为交错数组不是多维数组。 可以通过检索每个子数组的 Array.Length 属性值,来确定每个子数组中的元素数。 以下示例演示如何确定交错数组中的元素数。


Module Example
   Public Sub Main()
      Dim jagged = { ({1, 2}), ({2, 3, 4}), ({5, 6}), ({7, 8, 9, 10}) }
      Console.WriteLine($"The value of jagged.Length: {jagged.Length}.")
      Dim total = jagged.Length
      For ctr As Integer = 0 To jagged.GetUpperBound(0)
         Console.WriteLine($"Element {ctr + 1} has {jagged(ctr).Length} elements.") 
         total += jagged(ctr).Length 
      Next
      Console.WriteLine($"The total number of elements in the jagged array: {total}")
   End Sub
End Module
' The example displays the following output:
'     The value of jagged.Length: 4.
'     Element 1 has 2 elements.
'     Element 2 has 3 elements.
'     Element 3 has 2 elements.
'     Element 4 has 4 elements.
'     The total number of elements in the jagged array: 15

零长度数组

Visual Basic 区分未初始化的数组(其值为 Nothing 的数组)和零长度数组,后者也称为空数组(不包含元素的数组)。未初始化的数组是指尚未对其进行维度计算或已分配任何值的数组。 例如:

Dim arr() As String

零长度数组是使用维度 -1 声明的。 例如:

Dim arrZ(-1) As String

在下列情况下,你可能需要创建一个零长度数组:

  • 为避免出现 NullReferenceException 异常风险,代码必须访问 Array 类的成员(例如 LengthRank),或调用 UBound 之类的 Visual Basic 函数。

  • 无需出于特殊情况检查 Nothing,这样可以保持代码的简单性。

  • 你的代码与应用程序编程接口 (API) 交互,该接口要求你将一个零长度数组传递到一个或多个过程,或将一个零长度数组返回到一个或多个过程。

拆分数组

在某些情况下,你可能需要将单个数组拆分为多个数组。 这涉及到确定该数组的一个或多个拆分点,然后将该数组拆分为两个或更多个单独的数组。

备注

本部分不讨论如何基于某个分隔符将单个字符串拆分为字符串数组。 有关拆分字符串的信息,请参阅 String.Split 方法。

拆分数组的最常见条件是:

  • 数组中的元素数。 例如,你可能想要将包含超过指定数量的元素的某个数组拆分为大致相等的多个数组。 为此,可以使用 Array.LengthArray.GetLength 方法返回的值。

  • 用作分隔符的元素的值,该分隔符指示数组的拆分位置。 可以通过调用 Array.FindIndexArray.FindLastIndex 方法来搜索特定的值。

确定了作为数组拆分位置的一个或多个索引后,可以通过调用 Array.Copy 方法来创建单独的数组。

以下示例将一个数组拆分为大小大致相等的两个数组。 (如果数组元素的总数是奇数,则第一个数组的元素比第二个多 1 个。)


Module Example
   Public Sub Main()
      ' Create an array of 100 elements.
      Dim arr(99) As Integer
      ' Populate the array.
      Dim rnd As new Random()
      For ctr = 0 To arr.GetUpperBound(0)
         arr(ctr) = rnd.Next()
      Next
      
      ' Determine how many elements should be in each array.
      Dim divisor = 2
      Dim remainder As Integer
      Dim boundary = Math.DivRem(arr.GetLength(0), divisor, remainder)
            
      ' Copy the array.
      Dim arr1(boundary - 1 + remainder), arr2(boundary - 1) as Integer
      Array.Copy(arr, 0, arr1, 0, boundary + remainder)
      Array.Copy(arr, boundary + remainder, arr2, 0, arr.Length - boundary) 
   End Sub
End Module

以下示例根据是否存在值为“zzz”的元素(用作数组分隔符),将某个字符串数组拆分为两个数组。 新数组不包括含有分隔符的元素。


Module Example
   Public Sub Main()
      Dim rnd As New Random()
      
      ' Create an array of 100 elements.
      Dim arr(99) As String
      ' Populate each element with an arbitrary ASCII character.
      For ctr = 0 To arr.GetUpperBound(0)
         arr(ctr) = ChrW(Rnd.Next(&h21, &h7F))
      Next
      ' Get a random number that will represent the point to insert the delimiter.
      arr(rnd.Next(0, arr.GetUpperBound(0))) = "zzz"

      ' Find the delimiter.
      Dim location = Array.FindIndex(arr, Function(x) x = "zzz")

      ' Create the arrays.
      Dim arr1(location - 1) As String
      Dim arr2(arr.GetUpperBound(0) - location - 1) As String
      
      ' Populate the two arrays.
      Array.Copy(arr, 0, arr1, 0, location)
      Array.Copy(arr, location + 1, arr2, 0, arr.GetUpperBound(0) - location)
   End Sub
End Module

联接数组

还可以将多个数组合并成一个更大的数组。 为此,也可以使用 Array.Copy 方法。

注意

本部分不讨论如何将字符串数组联接成单个字符串。 有关联接字符串数组的信息,请参阅 String.Join 方法。

在将每个数组的元素复制到新数组之前,必须首先确保已初始化该新数组,使其有足够大的空间容纳这些元素。 可以通过两种方法执行此操作:

  • 在将新元素添加到数组之前,请使用 ReDim Preserve 语句动态扩展该数组。 这是最简单的方法,但在复制大数组时,这可能会导致性能降低和消耗过多的内存。
  • 计算新的大数组所需的元素总数,然后将每个源数组的元素添加到其中。

以下示例使用第二种方法将四个数组(每个数组包含 10 个元素)添加到单个数组中。

Imports System.Collections.Generic
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim tasks As New List(Of Task(Of Integer()))
      ' Generate four arrays.
      For ctr = 0 To 3
         Dim value = ctr
         tasks.Add(Task.Run(Function()
                               Dim arr(9) As Integer
                               For ndx = 0 To arr.GetUpperBound(0)
                                  arr(ndx) = value
                               Next
                               Return arr
                            End Function))   
       Next
       Task.WaitAll(tasks.ToArray())
       ' Compute the number of elements in all arrays.
       Dim elements = 0
       For Each task In tasks
          elements += task.Result.Length
       Next
       Dim newArray(elements - 1) As Integer
       Dim index = 0
       For Each task In tasks
          Dim n = task.Result.Length
          Array.Copy(task.Result, 0, newArray, index, n)
          index += n
       Next 
      Console.WriteLine($"The new array has {newArray.Length} elements.")
   End Sub
End Module
' The example displays the following output:
'     The new array has 40 elements.

由于在本例中源数组都很小,因此我们还可以在将每个新数组的元素添加到目标数组时动态扩展该数组。 以下示例执行该操作。

Imports System.Collections.Generic
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim tasks As New List(Of Task(Of Integer()))
      ' Generate four arrays.
      For ctr = 0 To 3
         Dim value = ctr
         tasks.Add(Task.Run(Function()
                               Dim arr(9) As Integer
                               For ndx = 0 To arr.GetUpperBound(0)
                                  arr(ndx) = value
                               Next
                               Return arr
                            End Function))   
       Next
       Task.WaitAll(tasks.ToArray())

       ' Dimension the target array and copy each element of each source array to it.
       Dim newArray() As Integer = {}
       ' Define the next position to copy to in newArray.
       Dim index = 0
       For Each task In tasks
          Dim n = Task.Result.Length
          ReDim Preserve newArray(newArray.GetUpperBound(0) + n)
          Array.Copy(task.Result, 0, newArray, index, n)
          index += n
       Next 
      Console.WriteLine($"The new array has {newArray.Length} elements.")
   End Sub
End Module
' The example displays the following output:
'     The new array has 40 elements.

可替代数组的集合

数组最适用于创建和使用固定数量的强类型化对象。 集合提供更灵活的方式来使用对象组。 与需要使用 ReDim 语句显式更改数组大小的数组不同,集合可以随着应用程序需求的变化而动态增长和缩小。

当你使用 ReDim 调整数组的维度时,Visual Basic 会创建一个新数组并释放前一个数组。 这需要执行时间。 因此,如果你处理的项数频繁变化,或者无法预测所需的最大项数,则使用集合通常可以获得更好的性能。

对于某些集合,你可以为放入集合中的任何对象分配一个密钥,这样你便可以使用该密钥快速检索此对象。

如果集合中只包含一种数据类型的元素,则可以使用 System.Collections.Generic 命名空间中的一个类。 泛型集合强制类型安全,因此无法向其添加任何其他数据类型。

有关集合的详细信息,请参阅集合

术语 定义
Array Dimensions in Visual Basic 在数组中解释级别和维度。
如何:在 Visual Basic 中初始化数组变量 说明如何用初始值填充数组。
如何:在 Visual Basic 中对数组进行排序 显示如何按字母先后顺序对数组元素进行排序。
如何:将一个数组赋给另一个数组 说明将数组分配到另一个数组变量的规则和步骤。
数组疑难解答 讨论在使用数组时出现的一些常见问题。

另请参阅