C# 在指定索引处从列表中删除元素的有效方法

C# 在指定索引处从列表中删除元素的有效方法,c#,linq,optimization,C#,Linq,Optimization,假设我有一个N个元素的列表(list1) 我有一张M的清单 或 bool[]shouldwesaybye=newbool[List1.Count]; 对于(var i=0;i您的最佳选择可能是坚持使用for循环。假设list2按相反顺序排序,它们将是最快的(例如) 您可以尝试使用RemoveAll也许 list1.RemoveAll(l1item => list2.Contains(list1.IndexOf(l1item))); 缺点是,虽然这看起来更干净、更直接,但其基础实际上相当复

假设我有一个N个元素的列表(
list1

我有一张M的清单 或

bool[]shouldwesaybye=newbool[List1.Count];

对于(var i=0;i您的最佳选择可能是坚持使用
for
循环。假设
list2
按相反顺序排序,它们将是最快的(例如)

您可以尝试使用
RemoveAll
也许

list1.RemoveAll(l1item => list2.Contains(list1.IndexOf(l1item)));
缺点是,虽然这看起来更干净、更直接,但其基础实际上相当复杂

public int RemoveAll(Predicate<T> match)
{
    if (match == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    int num = 0;
    while (num < this._size && !match(this._items[num]))
    {
        num++;
    }
    if (num >= this._size)
    {
        return 0;
    }
    int i = num + 1;
    while (i < this._size)
    {
        while (i < this._size && match(this._items[i]))
        {
            i++;
        }
        if (i < this._size)
        {
            this._items[num++] = this._items[i++];
        }
    }
    Array.Clear(this._items, num, this._size - num);
    int result = this._size - num;
    this._size = num;
    this._version++;
    return result;
}
这将执行一个
数组。每次删除一个项目时复制
,而
RemoveAll
将遍历项目,并将索引项目向上移动到“已删除”项目上

您可以在这两个平台上运行一些简单的基准测试,看看哪一个更好

不过,使用以下方法可能会获得最佳结果:

for (int i = 0; i < list1.Count; i++)
{
   bool exists = false;
   for (int j = 0; j < list2.Count; j++)
      if (i == list2[j]) 
      {
         exists = true;
         break;
      }
   if (!exists) newList.Add(list1[i]);
}
for(int i=0;i
这不应依赖于列表顺序。

列表。如果未超过容量,则添加实际上是
O(1)
。 下面是我想到的,应该在
O(n)

List resultList=新列表(list1.Count);//容量大!
int curIdx=list1.Count-1;//从list1的末尾开始
//假设列表2是按降序排序的
list2.Add(-1);//添加一个最终的-1索引,以使下面的代码更好
foreach(列表2中的int targetIdx)
{
while(curIdx>targetIdx)
{
Add(list1[curIdx]);//两个操作都是O(1)
库里克斯--;
}
库里克斯--;
}
//结果列表是颠倒的

澄清:

清单1:[10,11,12,13,14]
清单2:[1,3]
通缉结果:[10,12,14]

我们希望
list2
按desc排序,并在其中添加一个final-1:

清单2:[3,1,-1]


最终的结果实际上是相反的结果。之后可以在
O(n)
中反转列表,这样就不会改变总体复杂性,但是您可以进一步优化代码,这样最终列表的顺序实际上是正确的(家庭作业!)

如果订购了
list2
,只需使用第二个优化的解决方案:

var exceptIndex = 0;
var newList = new List<T>();

for (var i = 0; i < list1.Length; i++)
{
    if (i != list2[exceptIndex]) newList.Add(list1[i]);
    else exceptIndex++
}

return newList;
var exceptIndex=0;
var newList=新列表();
for(var i=0;i
优化是乐趣的根源,IMHO。过早与否!那么我们可以假设
list2
已经被排序了吗?我们可以假设很多关于
list2
的事情。我们可以假设它被排序了,或者它是一个
HashSet
。我可以控制
list2
以及它的创建方式,所以我可以对它做很多有趣的事情。Bu我不同意你的看法,优化很酷!你为什么要交叉?保留新创建的列表。@中间是因为起初它是一个例外,而不是一个中间部分,然后我就不知道我在做什么,但你是对的(尽管如此,这对我来说似乎不是最好的方法)使用
LinkedList
而不是
List
的第一种方法是
O(n)
,因为删除操作变为O(1)非常酷的回答,非常模糊。你介意解释一下为什么它会被执行吗?因为删除元素会迫使列表在它之后移动每个元素。生成一个新的列表要快得多。@Fylax这基本上是第二个解决方案的LINQ版本,删除了不必要的
,除了
@InBetween我弄明白了这是什么(你已经指出有一个超链接实际上毫无意义,我同意你的观点),但我仍然不清楚为什么它更高效。正如我在问题中所解释的,保存ns是一个巨大的改进,根据我的经验,LINQ往往比a for Loop慢。编辑后,没有理由编写新的答案。我的想法只是更改列表2的结构,使其成为您所称的
shallWeSayGoodby
m是开始。似乎是执行此任务的最有效和最干净的方式,尽管你在foreach中看到了一段时间-我们实际上只在n个元素上循环使用反向for循环将使我的最终列表按正确的顺序排列,我想。但最终顺序其实并不是什么大问题,正如我之前所说的,我做了很多随机访问(注意随机性),所以它只会增加熵。另外,我知道容量成本。我在代码中尝试了所有可能的技巧来提高性能:)无论如何,感谢大家,我找到了一个非常好和有效的解决方案(虽然它会大量改变列表2)@Fylax,愿意分享你的解决方案吗?当然,这是在我的计划中。谢谢。最后我意识到我将你的解决方案合并到一个新的解决方案中的方法:将list2作为一个布尔数组,表示哪些索引应该保留,哪些不应该保留。不过,这与我最初的问题相去甚远。
list1.RemoveAll(l1item => list2.Contains(list1.IndexOf(l1item)));
public int RemoveAll(Predicate<T> match)
{
    if (match == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    int num = 0;
    while (num < this._size && !match(this._items[num]))
    {
        num++;
    }
    if (num >= this._size)
    {
        return 0;
    }
    int i = num + 1;
    while (i < this._size)
    {
        while (i < this._size && match(this._items[i]))
        {
            i++;
        }
        if (i < this._size)
        {
            this._items[num++] = this._items[i++];
        }
    }
    Array.Clear(this._items, num, this._size - num);
    int result = this._size - num;
    this._size = num;
    this._version++;
    return result;
}
public void RemoveAt(int index)
{
    if (index >= this._size)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException();
    }
    this._size--;
    if (index < this._size)
    {
        Array.Copy(this._items, index + 1, this._items, index, this._size - index);
    }
    this._items[this._size] = default(T);
    this._version++;
}
for (int i = 0; i < list1.Count; i++)
{
   bool exists = false;
   for (int j = 0; j < list2.Count; j++)
      if (i == list2[j]) 
      {
         exists = true;
         break;
      }
   if (!exists) newList.Add(list1[i]);
}
List<T> resultList = new List<T>(list1.Count); // high capacity!
int curIdx = list1.Count - 1; // start at the end of list1

// assumes list2 is sorted descendingly
list2.Add(-1); // add a final -1 index to make following code nicer

foreach (int targetIdx in list2)
{
    while (curIdx > targetIdx)
    {
        resultList.Add(list1[curIdx]); // both operations are O(1)
        curIdx--;
    }
    curIdx--;
}

// resultList is reversed
var exceptIndex = 0;
var newList = new List<T>();

for (var i = 0; i < list1.Length; i++)
{
    if (i != list2[exceptIndex]) newList.Add(list1[i]);
    else exceptIndex++
}

return newList;