如何有效地从List <t>中删除(C#)?</t>

时间:2012-12-29 13:16:13

标签: c# .net generics collections generic-collections

如果我理解正确(如果我错了请纠正我),list是由.NET中的数组实现的,这意味着列表中每个项目的删除都会导致重新分配所有列表(反过来意味着O(n))。

我正在开发一款游戏,在游戏中我有很多子弹在任何给定时刻在空中飞行,让我们说100个子弹,每帧我移动它们几个像素并检查与游戏中物体的碰撞,我需要从列表中删除每个碰撞的子弹。

所以我在另一个临时列表中收集碰撞的子弹,然后执行以下操作:

foreach (Bullet bullet in bulletsForDeletion)
    mBullets.Remove(bullet);

由于循环为O(n)且删除为O(n),因此我需要花费O(n^2}时间来删除。

有没有更好的方法可以删除它,或者使用更合适的集合?

6 个答案:

答案 0 :(得分:6)

设置和链接列表都具有恒定的时间删除。你能使用这些数据结构吗?

无法避免从List<T>中移除O(N)费用。如果这对您来说是个问题,您将需要使用不同的数据结构。这可能会使计算bulletsToRemove的代码感觉更好。

ISet<T>有很好的方法来计算对象集之间的差异和交叉点。

你使用套装丢失了订单,但鉴于你正在使用子弹,我猜这不是问题。你仍然可以在不变的时间内枚举它。


在您的情况下,您可以写:

mBullets.ExceptWith(bulletsForDeletion);

答案 1 :(得分:4)

创建新列表:

var newList = `oldList.Except(deleteItems).ToList()`.

尝试尽可能使用功能习语。不要修改现有的数据结构,创建新的数据结构。

由于散列,该算法为O(N)。

答案 2 :(得分:2)

你不能只从List(相当于Java的ArrayList突出显示那个)切换到LinkedList吗? LinkedList使用O(1)删除特定元素,但O(n)删除索引

答案 3 :(得分:1)

如何在内部实施列表并不是您应该考虑的事情。您应该与该抽象级别中的列表进行交互。

如果您确实遇到了性能问题,并将其精确定位到列表中,那么现在是时候了解它是如何实现的。

至于从列表中删除项目 - 您可以使用mBullets.RemoveAll(predicate),其中predicate是标识已发生冲突的项目的表达式。

答案 4 :(得分:1)

RemoveAt(int index)Remove(T item)快,因为后者使用第一个内部进行反射,这是每个函数中的代码。

此外,Remove(T)具有函数IndexOf,其中包含第二个循环以评估每个项目的索引。

public bool Remove(T item)
{
  int index = this.IndexOf(item);
  if (index < 0)
    return false;
  this.RemoveAt(index);
  return true;
}


public void RemoveAt(int index)
{
  if ((uint) index >= (uint) this._size)
    ThrowHelper.ThrowArgumentOutOfRangeException();
  --this._size;
  if (index < this._size)
    Array.Copy((Array) this._items, index + 1, (Array) this._items, index, this._size - index);
  this._items[this._size] = default (T);
  ++this._version;
}

我会做这样的循环:

for (int i=MyList.Count-1; i>=0; i--) 
{
    // add some code here
    if (needtodelete == true)
    MyList.RemoveAt(i);
}

答案 5 :(得分:0)

根据“usr”建议的功能习语的精神,您可能会考虑不从列表中删除。相反,让您的更新例程采用列表并返回列表。返回的列表仅包含“仍然活着”的对象。然后,您可以在游戏循环结束时交换列表(或者,如果适用,立即交换)。我自己就是这样做的。