如何在VB.NET中从数组中删除一个项目?

32

如何从一个VB.NET数组中删除一个元素?

12个回答

18

正如Heinzi所说,数组具有固定的大小。为了“删除一个项”或“调整大小”,您需要创建一个具有所需大小的新数组,并适当地复制所需的项。

以下是从数组中删除一项的代码:

<System.Runtime.CompilerServices.Extension()> _
Function RemoveAt(Of T)(ByVal arr As T(), ByVal index As Integer) As T()
    Dim uBound = arr.GetUpperBound(0)
    Dim lBound = arr.GetLowerBound(0)
    Dim arrLen = uBound - lBound

    If index < lBound OrElse index > uBound Then
        Throw New ArgumentOutOfRangeException( _
        String.Format("Index must be from {0} to {1}.", lBound, uBound))

    Else
        'create an array 1 element less than the input array
        Dim outArr(arrLen - 1) As T
        'copy the first part of the input array
        Array.Copy(arr, 0, outArr, 0, index)
        'then copy the second part of the input array
        Array.Copy(arr, index + 1, outArr, index, uBound - index)

        Return outArr
    End If
End Function

你可以直接这样使用它:
Module Module1

    Sub Main()
        Dim arr = New String() {"abc", "mno", "xyz"}
        arr.RemoveAt(1)
    End Sub
End Module

上面的代码从数组中删除了第二个元素(索引为1的"mno")。

您需要在 .NET 3.5 或更高版本中开发才能使用该扩展方法。 如果您正在使用 .NET 2.0 或 3.0,则可以按如下方式调用该方法

arr = RemoveAt(arr, 1)

我希望这是你需要的。

更新

在根据ToolMakerSteve的评论运行测试后,发现最初的代码由于函数声明中使用了ByVal,无法修改要更新的数组。然而,写类似于arr = arr.RemoveAt(1)arr = RemoveAt(arr, 1)的代码可以修改数组,因为它将修改后的数组重新赋值给原始数组。

下面是更新后从数组中删除元素的方法(子程序)。

<System.Runtime.CompilerServices.Extension()> _
Public Sub RemoveAt(Of T)(ByRef arr As T(), ByVal index As Integer)
    Dim uBound = arr.GetUpperBound(0)
    Dim lBound = arr.GetLowerBound(0)
    Dim arrLen = uBound - lBound

    If index < lBound OrElse index > uBound Then
        Throw New ArgumentOutOfRangeException( _
        String.Format("Index must be from {0} to {1}.", lBound, uBound))

    Else
        'create an array 1 element less than the input array
        Dim outArr(arrLen - 1) As T
        'copy the first part of the input array
        Array.Copy(arr, 0, outArr, 0, index)
        'then copy the second part of the input array
        Array.Copy(arr, index + 1, outArr, index, uBound - index)

        arr = outArr
    End If
End Sub

这种方法的使用方式与原始方法类似,但这次没有返回值,因此尝试从返回值中分配数组将不起作用,因为没有返回任何内容。

Dim arr = New String() {"abc", "mno", "xyz"}
arr.RemoveAt(1)  ' Output: {"abc", "mno"} (works on .NET 3.5 and higher)
RemoveAt(arr, 1) ' Output: {"abc", "mno"} (works on all versions of .NET fx)
arr = arr.RemoveAt(1)  'will not work; no return value
arr = RemoveAt(arr, 1) 'will not work; no return value

注意:

  1. 我在这个过程中使用了一个临时数组,因为这使我的意图清晰明确,而这正是VB.NET在幕后执行的操作,当你使用Redim Preserve时。如果您想使用Redim Preserve原地修改数组,请参见ToolmakerSteve的答案

  2. 这里编写的RemoveAt方法是扩展方法。为了使它们起作用,您必须将它们粘贴到一个Module中。如果将扩展方法放置在Class中,则无法在VB.NET中使用。

  3. 重要提示: 如果您将使用大量“删除”来修改数组,则强烈建议使用其他数据结构,例如此问题的其他回答者建议的List(Of T)


1
Alex的答案展示了这个技巧,但是因为他传递的是ByVal,调用者的数组变量并没有被改变。正确的用法应该是“arr = arr.RemoveAt(1)”。或者看看我的答案,它有一个替代实现,仅需“arr.RemoveAt(1)”即可工作。如果你想避免覆盖旧数组(例如,“arr2 = arr1.RemoveAt(1)”),或者喜欢一种编码风格,使得显然这是在修改数组,则Alex的方法更可取。 - ToolmakerSteve

12

无法删除数组元素,建议将数组元素放入List中,至少你可以删除其中的项。可以使用ReDim扩展数组,但是一旦创建了数组元素就无法删除,必须从头开始重建数组。

如果可能的话,不要在此处使用数组,而要使用List


10

LINQ 的一行代码:

Dim arr() As String = {"uno", "dos", "tres", "cuatro", "cinco"}
Dim indx As Integer = 2
arr = arr.Where(Function(item, index) index <> indx).ToArray 'arr = {"uno", "dos", "cuatro", "cinco"}

删除第一个元素:

arr = arr.Skip(1).ToArray

删除最后一个元素:

arr = arr.Take(arr.length - 1).ToArray

5
这要看你所说的“删除”是什么意思。数组具有固定大小,因此删除实际上没有意义。
如果您想要删除元素 i ,则一种选择是将所有元素 j> i 向左移动一个位置(对于所有 j a [j-1] = a [j] 或使用 Array.Copy ),然后使用 ReDim Preserve 调整数组大小。
因此,除非您被某些外部约束迫使使用数组,请考虑使用更适合添加和删除项目的数据结构。例如, List<T>也在内部使用数组,但会处理所有调整大小问题:对于删除项目,它使用上面提到的算法(不使用ReDim),这就是为什么 List<T>.RemoveAt 是O(n)操作的原因。
System.Collections.Generic 命名空间中还有很多不同的集合类,针对不同的用例进行优化。如果经常删除项目是一个要求,则有很多比数组(甚至 List<T>)更好的选择。

5

是的,你可以从一个数组中删除一个元素。以下是一个扩展方法,它根据需要移动元素,然后将数组大小调整为较短:

' Remove element at index "index". Result is one element shorter.
' Similar to List.RemoveAt, but for arrays.
<System.Runtime.CompilerServices.Extension()> _
Public Sub RemoveAt(Of T)(ByRef a() As T, ByVal index As Integer)
    ' Move elements after "index" down 1 position.
    Array.Copy(a, index + 1, a, index, UBound(a) - index)
    ' Shorten by 1 element.
    ReDim Preserve a(UBound(a) - 1)
End Sub

使用示例(假设数组从索引0开始):

Dim a() As String = {"Albert", "Betty", "Carlos", "David"}
a.RemoveAt(0)    ' Remove first element => {"Betty", "Carlos", "David"}
a.RemoveAt(1)    ' Remove second element => {"Betty", "David"}
a.RemoveAt(UBound(a))    ' Remove last element => {"Betty"}

删除第一个或最后一个元素很常见,因此这里提供了方便的例程来实现(我喜欢代码更加易读地表达我的意图):

<System.Runtime.CompilerServices.Extension()> _
Public Sub DropFirstElement(Of T)(ByRef a() As T)
    a.RemoveAt(0)
End Sub

<System.Runtime.CompilerServices.Extension()> _
Public Sub DropLastElement(Of T)(ByRef a() As T)
    a.RemoveAt(UBound(a))
End Sub

使用方法:

a.DropFirstElement()
a.DropLastElement()

正如Heinzi所说,如果你发现自己在这样做,请尽可能使用List(Of T)。 List已经有“RemoveAt”子程序,以及其他有用于插入/删除元素的例程。


3

我最喜欢的方式是:

Imports System.Runtime.CompilerServices

<Extension()> _
Public Sub RemoveAll(Of T)(ByRef arr As T(), matching As Predicate(Of T))
    If Not IsNothing(arr) Then
        If arr.Count > 0 Then
            Dim ls As List(Of T) = arr.ToList
            ls.RemoveAll(matching)
            arr = ls.ToArray
        End If
    End If
End Sub

在代码中,每当我需要从数组中删除某些内容时,可以通过该数组中某个对象的某个属性具有特定值来实现,例如:

arr.RemoveAll(Function(c) c.MasterContactID.Equals(customer.MasterContactID))

或者,如果我已经知道要删除的确切对象,可以直接执行以下操作:

arr.RemoveAll(function(c) c.equals(customer))

2
变量i代表你想要删除的元素的索引:
System.Array.Clear(ArrayName, i, 1)

对我来说,System.Array.Clear只清除数组项,而不是删除它。例如,在我的字符串数组中,它将项目变为空字符串并保留在数组中。我认为最有效的方法是将数组复制到List中,然后从列表中删除该项,最后将列表复制回数组中。 - Mark Entingh

1
Public Sub ArrayDelAt(ByRef x As Array, ByVal stack As Integer)
    For i = 0 To x.Length - 2
        If i >= stack Then
            x(i) = x(i + 1)
            x(x.Length-1) = Nothing
        End If
    Next
End Sub

尝试这个。

仅仅是代码转储而没有任何解释并不会有太多帮助。 - vonbrand

1
这可能是一种懒人的解决方案,但你可以通过将要删除的索引的内容重新赋值为0或""来删除它们的内容,然后忽略/跳过这些空数组元素,而不是不停地创建和复制数组。

0
如果数组是字符串数组,那么你可以执行以下操作:
AlphaSplit = "a\b\c".Split("\")
MaxIndex   = AlphaSplit.GetUpperBound(0)
AlphaSplit = AlphaSplit.Where(Function(item, index) index <> MaxIndex).ToArray
AlphaJoin  = String.Join("\", PublishRouteSplit)

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