删除给定索引处的列表元素

本文关键字:列表元素 索引 删除 | 更新日期: 2023-09-27 18:29:11

我有一个列表,其中包含一些字符串类型的项。

List<string> lstOriginal;

我有另一个列表,其中包含应从第一个列表中删除的ID。

List<int> lstIndices;

我试着用RemoveAt()方法来完成这项工作

foreach(int indice in lstIndices)
{
     lstOriginal.RemoveAt(indice);
}

但它崩溃了,并告诉我"索引超出范围"。

删除给定索引处的列表元素

您需要将要返回的索引从最大到最小排序,以避免在错误的索引中删除某些内容。

foreach(int indice in lstIndices.OrderByDescending(v => v))
{
     lstOriginal.RemoveAt(indice);
}

原因如下:假设有一个由五个项目组成的列表,并且您希望删除索引24处的项目。如果首先删除2处的项目,则索引4处的项目将位于索引3处,索引4将不再在列表中(导致异常)。如果向后看,所有索引都会一直存在,直到您准备删除相应的项为止。

如何填充索引列表?您可能可以使用一种更有效的RemoveAll方法。例如,代替这个:

var indices = new List<int>();
int index = 0;
foreach (var item in data)
    if (SomeFunction(data))
        indices.Add(index++);
//then some logic to remove the items

你可以这样做:

data.RemoveAll(item => SomeFunction(item));

这最大限度地减少了将项目复制到阵列中的新位置;每个项目只复制一次。

您也可以在上面的例子中使用方法组转换,而不是lambda:

data.RemoveAll(SomeFunction);

之所以会发生这种情况,是因为当您从列表中删除一个项目时,每个项目之后的索引都会有效地减少一,因此,如果您按索引递增的顺序删除它们,并且原始列表末尾附近的一些项目将被删除,那么这些索引现在是无效的,因为随着较早项目的删除,列表会变短。

最简单的解决方案是按递减顺序对索引列表进行排序(首先是最高索引),然后进行迭代。

for (int i = 0; i < indices.Count; i++)
{
    items.RemoveAt(indices[i] - i);
}

My就地删除给定索引是一种方便的扩展方法。它只复制所有项目一次,因此如果要删除大量标记,它的性能会高得多。

在要移除的索引超出界限的情况下,它还会抛出ArgumentOutOfRangeException

 public static class ListExtensions 
 {
    public static void RemoveAllIndices<T>(this List<T> list, IEnumerable<int> indices)
    {
        //do not remove Distinct() call here, it's important
        var indicesOrdered = indices.Distinct().ToArray();
        if(indicesOrdered.Length == 0)
            return;
        Array.Sort(indicesOrdered);
        if (indicesOrdered[0] < 0 || indicesOrdered[indicesOrdered.Length - 1] >= list.Count)
            throw new ArgumentOutOfRangeException();
        int indexToRemove = 0;
        int newIdx = 0;
        for (int originalIdx = 0; originalIdx < list.Count; originalIdx++)
        {
            if(indexToRemove < indicesOrdered.Length && indicesOrdered[indexToRemove] == originalIdx)
            {
                indexToRemove++;
            }
            else
            {
                list[newIdx++] = list[originalIdx];
            }
        }
        list.RemoveRange(newIdx, list.Count - newIdx);
    }
}
        var array = lstOriginal.ConvertAll(item => new int?(item)).ToArray();
        lstIndices.ForEach(index => array[index] = null);
        lstOriginal = array.Where(item => item.HasValue).Select(item => item.Value).ToList();
 lstIndices.OrderByDescending(p => p).ToList().ForEach(p => lstOriginal.RemoveAt((int)p));

附带说明一下,在foreach语句中,最好不要修改foreach运行的Ienumerable。超出范围的误差可能是这种情况的结果。