删除常规数组的元素

Remove element of a regular array

提问人:leora 提问时间:1/19/2009 最后编辑:Peter Mortensenleora 更新时间:11/3/2023 访问量:518115

问:

我有一个 Foo 对象数组。如何删除数组的第二个元素?

我需要类似于但用于常规数组的东西。RemoveAt()

C# .NET 数组

评论

1赞 abatishchev 1/19/2009
用。System.Collections.ObjectModel.Collection<Foo>
2赞 Krythic 9/17/2016
对于我的游戏,我使用了“索引处为空”的数据结构。基本上,内部数组(buffer)是静态大小的,我没有删除索引并调整数组大小,而是使索引为空。当我需要添加一个项目时,我只需找到第一个非空索引并将其放置在那里。效果很好,但显然不是所有事情。

答:

1赞 Paul Mitchell 1/19/2009 #1

这是我是如何做到的......

    public static ElementDefinitionImpl[] RemoveElementDefAt(
        ElementDefinition[] oldList,
        int removeIndex
    )
    {
        ElementDefinitionImpl[] newElementDefList = new ElementDefinitionImpl[ oldList.Length - 1 ];

        int offset = 0;
        for ( int index = 0; index < oldList.Length; index++ )
        {
            ElementDefinitionImpl elementDef = oldList[ index ] as ElementDefinitionImpl;
            if ( index == removeIndex )
            {
                //  This is the one we want to remove, so we won't copy it.  But 
                //  every subsequent elementDef will by shifted down by one.
                offset = -1;
            }
            else
            {
                newElementDefList[ index + offset ] = elementDef;
            }
        }
        return newElementDefList;
    }
81赞 Sebastian Dietz 1/19/2009 #2

数组的本质是它们的长度是不可变的。不能添加或删除任何数组项。

您必须创建一个短一个元素的新数组,并将旧项目复制到新数组中,但不包括要删除的元素。

因此,最好使用 List 而不是数组。

评论

4赞 Immortal Blue 2/15/2013
将数组转换为列表List<mydatatype> array = new List<mydatatype>(arrayofmydatatype)
1赞 Dyndrilliac 6/1/2019
@ImmortalBlue或仅使用命名空间中的方法。var myList = myArray.ToList();Enumerable.ToList()System.Linq
1赞 gkrogers 1/19/2009 #3

在普通数组中,必须将所有大于 2 的数组条目洗牌,然后使用 Resize 方法调整其大小。最好使用 ArrayList。

235赞 Andrew Kennan 1/19/2009 #4

如果您不想使用 List:

var foos = new List<Foo>(array);
foos.RemoveAt(index);
return foos.ToArray();

您可以尝试这个我尚未实际测试的扩展方法:

public static T[] RemoveAt<T>(this T[] source, int index)
{
    T[] dest = new T[source.Length - 1];
    if( index > 0 )
        Array.Copy(source, 0, dest, 0, index);

    if( index < source.Length - 1 )
        Array.Copy(source, index + 1, dest, index, source.Length - index - 1);

    return dest;
}

并像这样使用它:

Foo[] bar = GetFoos();
bar = bar.RemoveAt(2);

评论

10赞 Martin Brown 1/20/2009
这个答案中给出的第一个例子比第二个例子效率低得多。它需要两个数组副本和索引后所有内容的移动,而不是一个选择性数组副本。
2赞 shahjapan 12/18/2009
+1 当然,但我们也可以使用 list OR List<Foo> list = new List<Foll>(GetFoos());列表。删除(my_foo);列表。删除At(2);其中 GetFoos() 将返回 Foos !!! 数组
2赞 Nelson 8/7/2010
方法中的第一行应显示“源。Length“而不是”array”。长度'。
1赞 bkqc 8/6/2016
此外,请记住,存储对原始数组的引用的任何变量将继续包含原始数据,并且源数组中的数组与输出数组之间的任何引用相等性比较都将返回负数。
1赞 krowe2 1/26/2017
@MartinBrown 实际上,将列表转换为 \from 和数组比数组复制要慢得多(数组复制只需几条 ASM 指令即可以 CPU 允许的最大速度复制数据)。此外,移动列表的速度非常快,因为它只需交换几个指针并删除节点数据(在这种情况下,节点数据只有 8 个字节 [加上另外 16 个字节用于头部\尾部指针])。
6赞 Martin Brown 1/19/2009 #5

这是我拥有的旧版本,它适用于 .NET Framework 1.0 版,不需要泛型类型。

public static Array RemoveAt(Array source, int index)
{
    if (source == null)
        throw new ArgumentNullException("source");

    if (0 > index || index >= source.Length)
        throw new ArgumentOutOfRangeException("index", index, "index is outside the bounds of source array");

    Array dest = Array.CreateInstance(source.GetType().GetElementType(), source.Length - 1);
    Array.Copy(source, 0, dest, 0, index);
    Array.Copy(source, index + 1, dest, index, source.Length - index - 1);

    return dest;
}

这是这样用的:

class Program
{
    static void Main(string[] args)
    {
        string[] x = new string[20];
        for (int i = 0; i < x.Length; i++)
            x[i] = (i+1).ToString();

        string[] y = (string[])MyArrayFunctions.RemoveAt(x, 3);

        for (int i = 0; i < y.Length; i++)
            Console.WriteLine(y[i]);
    }
}
72赞 EdHellyer 12/13/2010 #6

我使用此方法从对象数组中删除元素。在我的情况下,我的数组长度很小。因此,如果您有大型阵列,则可能需要另一种解决方案。

private int[] RemoveIndices(int[] IndicesArray, int RemoveAt)
{
    int[] newIndicesArray = new int[IndicesArray.Length - 1];

    int i = 0;
    int j = 0;
    while (i < IndicesArray.Length)
    {
        if (i != RemoveAt)
        {
            newIndicesArray[j] = IndicesArray[i];
            j++;
        }

        i++;
    }

    return newIndicesArray;
}

评论

8赞 oillio 3/9/2011
就个人而言,我更喜欢这个答案而不是公认的答案。它应该同样高效,并且更容易阅读。我可以看它,知道它是正确的。我必须测试另一个,以确保这些副本被正确编写。
1赞 Sepulchritude 5/23/2012
真的很遗憾,这个答案太低了,而它比上面的两个要好得多。
0赞 Jordi Huertas 3/1/2019
啊,这就是我一直在寻找的答案!这是没有列表的最佳方法。
5赞 nawfal 10/8/2011 #7

不完全是解决这个问题的方法,但如果情况微不足道,并且你珍惜你的时间,你可以尝试这个可为 null 的类型。

Foos[index] = null

然后检查逻辑中的 null 条目。.

评论

0赞 Krythic 9/17/2016
这就是我为我的游戏所做的。对于经常更改的区域,请使用可为 null 的缓冲区。
-4赞 Bamara Coulibaly 4/17/2012 #8

第一步
你需要将数组转换成一个列表,你可以写一个这样的扩展方法

// Convert An array of string  to a list of string
public static List<string> ConnvertArrayToList(this string [] array) {

    // DECLARE a list of string and add all element of the array into it

    List<string> myList = new List<string>();
    foreach( string s in array){
        myList.Add(s);
    }
    return myList;
} 

第二步
:编写扩展方法,将列表转换回数组

// convert a list of string to an array 
public static string[] ConvertListToArray(this List<string> list) {

    string[] array = new string[list.Capacity];
    array = list.Select(i => i.ToString()).ToArray();
    return array;
}

最后一步
编写最终方法,但请记住在转换回数组之前删除索引处的元素,如代码所示

public static string[] removeAt(string[] array, int index) {

    List<string> myList = array.ConnvertArrayToList();
    myList.RemoveAt(index);
    return myList.ConvertListToArray();
} 

示例代码可以在我的博客上找到,请继续跟踪。

评论

14赞 user7116 7/25/2013
考虑到存在和采用现有序列的构造函数,这有点疯狂.......ToArray()List<T>
12赞 infografnet 7/25/2013 #9

从 .Net 3.5 开始,这是一种删除数组元素的方法,而无需复制到另一个数组 - 使用具有 Array.Resize<T> 的相同数组实例:

public static void RemoveAt<T>(ref T[] arr, int index)
{
    for (int a = index; a < arr.Length - 1; a++)
    {
        // moving elements downwards, to fill the gap at [index]
        arr[a] = arr[a + 1];
    }
    // finally, let's decrement Array's size by one
    Array.Resize(ref arr, arr.Length - 1);
}

评论

3赞 Jon Schneider 9/11/2014
“不复制到另一个数组” - 根据链接的文档,Array.Resize 实际上确实在幕后分配了一个新数组,并将元素从旧数组复制到新数组。不过,我喜欢这个解决方案的简洁性。
0赞 Darren 1/3/2015
非常好,如果你确定它是一个相对较小的数组。
1赞 Jeppe Stig Nielsen 9/18/2015
继续@JonSchneider的评论,它不是“同一个数组实例”。这就是调用该方法时需要使用的原因。数组实例的长度是固定且不可变的。refResize
2赞 Bartel 9/24/2019
如果元素的顺序不重要,则可以将索引处的元素与最后一个元素交换,然后调整大小,而不是向下移动所有元素,然后调整大小:arr[index] = arr[arr.长度 - 1];Array.Resize(ref arr, arr.长度 - 1);
2赞 Duncan 1/3/2014 #10

像往常一样,我参加聚会迟到了......

我想在已经存在的不错的解决方案列表中添加另一个选项。=)
我认为这是扩展的好机会。

参考资料: http://msdn.microsoft.com/en-us/library/bb311042.aspx

因此,我们定义了一些静态类,并在其中定义了我们的方法。
之后,我们可以随意使用我们的扩展方法。=)

using System;

namespace FunctionTesting {

    // The class doesn't matter, as long as it's static
    public static class SomeRandomClassWhoseNameDoesntMatter {

        // Here's the actual method that extends arrays
        public static T[] RemoveAt<T>( this T[] oArray, int idx ) {
            T[] nArray = new T[oArray.Length - 1];
            for( int i = 0; i < nArray.Length; ++i ) {
                nArray[i] = ( i < idx ) ? oArray[i] : oArray[i + 1];
            }
            return nArray;
        }
    }

    // Sample usage...
    class Program {
        static void Main( string[] args ) {
            string[] myStrArray = { "Zero", "One", "Two", "Three" };
            Console.WriteLine( String.Join( " ", myStrArray ) );
            myStrArray = myStrArray.RemoveAt( 2 );
            Console.WriteLine( String.Join( " ", myStrArray ) );
            /* Output
             * "Zero One Two Three"
             * "Zero One Three"
             */

            int[] myIntArray = { 0, 1, 2, 3 };
            Console.WriteLine( String.Join( " ", myIntArray ) );
            myIntArray = myIntArray.RemoveAt( 2 );
            Console.WriteLine( String.Join( " ", myIntArray ) );
            /* Output
             * "0 1 2 3"
             * "0 1 3"
             */
        }
    }
}
58赞 Jon Schneider 9/12/2014 #11

LINQ 单线解决方案:

myArray = myArray.Where((source, index) => index != 1).ToArray();

在该示例中,是要删除的元素的索引 -- 在此示例中,根据原始问题,第 2 个元素(在 C# 从零开始的数组索引中是第二个元素)。11

一个更完整的例子:

string[] myArray = { "a", "b", "c", "d", "e" };
int indexToRemove = 1;
myArray = myArray.Where((source, index) => index != indexToRemove).ToArray();

运行该代码段后,的值将为 。myArray{ "a", "c", "d", "e" }

评论

2赞 Krythic 9/17/2016
对于需要高性能/频繁访问的区域,不推荐使用 LINQ。
3赞 Jon Schneider 9/19/2016
@Krythic 这是一个公平的评论。该解决方案在紧密循环中运行数千次,其性能不如本页上其他一些投票很高的解决方案:dotnetfiddle.net/z9Xkpn
1赞 user2884232 6/22/2015 #12
    private int[] removeFromArray(int[] array, int id)
    {
        int difference = 0, currentValue=0;
        //get new Array length
        for (int i=0; i<array.Length; i++)
        {
            if (array[i]==id)
            {
                difference += 1;
            }
        }
        //create new array
        int[] newArray = new int[array.Length-difference];
        for (int i = 0; i < array.Length; i++ )
        {
            if (array[i] != id)
            {
                newArray[currentValue] = array[i];
                currentValue += 1;
            }
        }

        return newArray;
    }

评论

0赞 AntonK 3/29/2023
请注意,原始问题是关于按索引删除一个元素,而此解决方案按值删除元素
0赞 user1618054 4/23/2017 #13

这是我根据一些现有答案制作的一小部分帮助程序方法。它利用扩展和静态方法以及参考参数来实现最大理想性:

public static class Arr
{
    public static int IndexOf<TElement>(this TElement[] Source, TElement Element)
    {
        for (var i = 0; i < Source.Length; i++)
        {
            if (Source[i].Equals(Element))
                return i;
        }

        return -1;
    }

    public static TElement[] Add<TElement>(ref TElement[] Source, params TElement[] Elements)
    {
        var OldLength = Source.Length;
        Array.Resize(ref Source, OldLength + Elements.Length);

        for (int j = 0, Count = Elements.Length; j < Count; j++)
            Source[OldLength + j] = Elements[j];

        return Source;
    }

    public static TElement[] New<TElement>(params TElement[] Elements)
    {
        return Elements ?? new TElement[0];
    }

    public static void Remove<TElement>(ref TElement[] Source, params TElement[] Elements)
    {
        foreach (var i in Elements)
            RemoveAt(ref Source, Source.IndexOf(i));
    }

    public static void RemoveAt<TElement>(ref TElement[] Source, int Index)
    {
        var Result = new TElement[Source.Length - 1];

        if (Index > 0)
            Array.Copy(Source, 0, Result, 0, Index);

        if (Index < Source.Length - 1)
            Array.Copy(Source, Index + 1, Result, Index, Source.Length - Index - 1);

        Source = Result;
    }
}

性能方面,它不错,但可能会有所改进。 依赖,并通过调用 为要删除的每个元素创建一个新数组。RemoveIndexOfRemoveAt

IndexOf是唯一的扩展方法,因为它不需要返回原始数组。 接受某种类型的多个元素以生成该类型的新数组。所有其他方法都必须接受原始数组作为引用,因此无需在之后分配结果,因为这已经在内部发生。New

我会定义一个合并两个数组的方法;但是,这已经可以通过传入实际数组而不是多个单个元素来使用 method 来实现。因此,可以采用以下两种方式来连接两组元素:MergeAddAdd

Arr.Add<string>(ref myArray, "A", "B", "C");

Arr.Add<string>(ref myArray, anotherArray);
-2赞 commandertuna 2/6/2019 #14

我知道这篇文章已经有十年的历史了,因此可能已经死了,但这是我会尝试做的事情:

使用 System.Linq 中的 IEnumerable.Skip() 方法。它将跳过数组中的选定元素,并返回数组的另一个副本,该副本仅包含除所选对象之外的所有内容。然后,只需对要删除的每个元素重复此操作,然后将其保存到变量中。

例如,如果我们有一个名为“Sample”(类型为 int[])的数组,其中包含 5 个数字。我们想删除第二个数组,所以尝试 “Sample.Skip(2);” 应该返回相同的数组,但没有第二个数字。

评论

1赞 xnr_z 11/11/2019
这个方法不是只是绕过序列中指定数量的元素,然后返回剩余的元素吗?在您的示例中,您将“跳过”泛型列表的前两个元素,而不仅仅是第二个元素!
3赞 Genci Ymeri 9/22/2019 #15

请尝试以下代码:

myArray = myArray.Where(s => (myArray.IndexOf(s) != indexValue)).ToArray();

myArray = myArray.Where(s => (s != "not_this")).ToArray();
0赞 James 11/3/2023 #16

嗯。为什么每个人都要分配一个新阵列?

只需将“removed”元素后面的部分向下复制一个索引,并将 null 或 default 放在最终元素中。

然后,您可以获得固定长度数组的好处(如果不使用列表,可能很重要),同时也不需要遍历每个元素检查 null(在第一个 null 时中断循环)。