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

时间:2012-03-28 13:29:51

标签: c# list

我有一个列表,其中包含一些string类型的项目。

List<string> lstOriginal;

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

List<int> lstIndices;

我尝试用 RemoveAt()方法完成这项工作,

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

但它崩溃并且告诉我“索引超出范围。”

7 个答案:

答案 0 :(得分:30)

您需要对要从最大到最小返回的索引进行排序,以避免删除错误索引处的内容。

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

原因如下:假设有一个包含五个项目的列表,并且您要删除索引24的项目。如果您首先删除2处的项目,则索引为4的项目将位于索引3,而索引4将不再位于列表中(导致你的例外)。如果你倒退,所有索引都会存在,直到你准备删除相应的项目为止。

答案 1 :(得分:6)

您如何填充指数列表?您可以使用更高效的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);

答案 2 :(得分:5)

发生这种情况的原因是,当您从列表中删除某个项目时,每个项目的索引会有效地减少一个,因此如果您按增加的索引顺序删除它们,而某些项目接近原始列表的末尾要删除,这些索引现在无效,因为删除前面的项目后列表会变短。

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

答案 3 :(得分:5)

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

答案 4 :(得分:1)

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

答案 5 :(得分:1)

我就地删除给定索引作为方便的扩展方法。它只复制所有项目一次,因此如果要移除大量的标记,它会更加高效。

如果要删除的索引超出范围,它也会抛出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);
    }
}

答案 6 :(得分:-2)

 lstIndices.OrderByDescending(p => p).ToList().ForEach(p => lstOriginal.RemoveAt((int)p));

作为旁注,在foreach语句中,最好不要修改运行foreach的Ienumerable。超出范围的错误可能是由于这种情况造成的。