【问题标题】:Remove element of a regular array删除常规数组的元素
【发布时间】:2009-01-19 12:36:38
【问题描述】:

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

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

【问题讨论】:

  • 使用System.Collections.ObjectModel.Collection<Foo>
  • 对于我的游戏,我使用了“索引空”数据结构。基本上,内部数组(缓冲区)是静态大小的,而不是删除索引和调整数组大小,我只是让索引为空。当我需要添加一个项目时,我只需找到第一个非空索引并将其放置在那里。效果很好,但显然不是所有的。

标签: c# .net arrays


【解决方案1】:

如果你不想使用 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);

【讨论】:

  • 这个答案中给出的第一个例子比第二个例子效率低得多。它需要两个数组副本和索引后的所有内容的移位,而不是一个选择性的数组副本。
  • +1 当然,但我们也可以使用 list 或 List list = new List(GetFoos()); list.Remove(my_foo); list.RemoveAt(2);其中 GetFoos() 将返回 Foos 数组!!!!
  • 方法内的第一行应该说 'source.Length' 而不是 'array.Length'。
  • 另外,请记住,存储对原始数组的引用的任何变量都将继续包含原始数据,并且源数组和输出数组之间的任何引用相等比较都将返回负数。
  • @MartinBrown 实际上,将列表转换为\from 和数组比数组复制要慢得多(它能够以 CPU 允许的最大速度复制数据,只需几个 ASM 指令) .此外,移动列表非常快,因为它只是交换几个指针并删除节点数据(在这种情况下只有 8 个字节[加上另外 16 个用于头\尾指针])。
【解决方案2】:

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

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

所以最好使用列表而不是数组。

【讨论】:

  • 将数组转换为列表List&lt;mydatatype&gt; array = new List&lt;mydatatype&gt;(arrayofmydatatype)
  • @ImmortalBlue 或只是 var myList = myArray.ToList(); 使用来自 System.Linq 命名空间的 Enumerable.ToList() 方法。
【解决方案3】:

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

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;
}

【讨论】:

  • 就个人而言,我更喜欢这个答案而不是接受的答案。它应该同样有效,并且更容易阅读。我可以看看它并知道它是正确的。我必须测试另一个以确保正确写入这些副本。
  • 真可惜这个答案太低了,远远好于上面的两个。
  • Aaarhg,这就是我正在寻找的答案!这是没有列表的最佳方法。
【解决方案4】:

LINQ 单行解决方案:

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

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

一个更完整的例子:

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

运行 sn-p 后,myArray 的值将是 { "a", "c", "d", "e" }

【讨论】:

  • 对于需要高性能/频繁访问的领域,不推荐使用LINQ。
  • @Krythic 这是一个中肯的评论。在一个紧密的循环中运行数千次,这个解决方案的性能不如这个页面上其他一些高票数的解决方案:dotnetfiddle.net/z9Xkpn
【解决方案5】:

这是一种删除数组元素的方法,从 .Net 3.5 开始,无需复制到另一个数组 - 使用与 Array.Resize&lt;T&gt; 相同的数组实例:

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);
}

【讨论】:

  • “不复制到另一个数组” - 根据链接的文档,Array.Resize 实际上确实在幕后分配了一个新数组,并将旧数组中的元素复制到新的那一个。不过,我喜欢这个解决方案的简洁性。
  • 如果你确定它是一个相对较小的数组,非常好和清晰。
  • 继续@JonSchneider 的评论,它不是“同一个数组实例”。这就是为什么在调用Resize 方法时需要使用ref 的原因。数组实例的长度是固定不变的。
  • 如果元素的顺序不重要,而不是向下移动所有元素,您可以将索引处的元素与最后一个元素交换,然后调整大小: arr[index] = arr[arr.Length - 1]; Array.Resize(ref arr, arr.Length - 1);
【解决方案6】:

这是我的旧版本,适用于 .NET 框架的 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]);
    }
}

【讨论】:

    【解决方案7】:

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

    Foos[index] = null
    

    然后检查逻辑中的空条目..

    【讨论】:

    • 这就是我为我的游戏所做的。为频繁更改的区域使用可为空的缓冲区。
    【解决方案8】:

    像往常一样,我迟到了……

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

    参考: 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"
                 */
            }
        }
    }
    

    【讨论】:

      【解决方案9】:

      试试下面的代码:

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

      myArray = myArray.Where(s => (s != "not_this")).ToArray();
      

      【讨论】:

        【解决方案10】:

        我是这样做的……

            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;
            }
        

        【讨论】:

          【解决方案11】:

          在普通数组中,您必须将 2 以上的所有数组条目打乱,然后使用 Resize 方法调整其大小。使用 ArrayList 可能会更好。

          【讨论】:

            【解决方案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;
                }
            

            【讨论】:

              【解决方案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;
                  }
              }
              

              在性能方面,它很不错,但可能会有所改进。 Remove 依赖于 IndexOf,并通过调用 RemoveAt 为您希望删除的每个元素创建一个新数组。

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

              我会定义一个Merge 方法来合并两个数组;但是,这已经可以通过 Add 方法通过传入一个实际数组而不是多个单独的元素来完成。所以Add可以用以下两种方式来连接两组元素:

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

              或者

              Arr.Add<string>(ref myArray, anotherArray);
              

              【讨论】:

                【解决方案14】:

                我知道这篇文章已有 10 年历史了,因此可能已经死了,但我会尝试这样做:

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

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

                【讨论】:

                • 这个方法不就是绕过序列中指定数量的元素,然后返回剩余的元素吗?在您的示例中,您将“跳过”通用列表的前两个元素,而不仅仅是第二个!
                【解决方案15】:

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

                // 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();
                } 
                

                示例代码可以在my blog找到,继续跟踪。

                【讨论】:

                • 考虑到 .ToArray() 和采用现有序列的 List&lt;T&gt; 构造函数的存在,这有点疯狂......
                猜你喜欢
                • 2019-04-01
                • 1970-01-01
                • 1970-01-01
                • 2011-04-08
                • 2020-09-07
                • 1970-01-01
                • 2023-03-09
                相关资源
                最近更新 更多