循环浏览列表和删除项目的正确方法

本文关键字:方法 删除项目 浏览 列表 循环 | 更新日期: 2023-09-27 18:32:11

我写了一个函数来遍历列表,并在满足某些条件时删除列表项。我的程序崩溃了,过了一会儿,我得出结论,外部的for循环遍历了列表中的所有项目。在同一个例程中,项目列表可能会变短。

 // Lijst is a list of a struct that contains a value .scanned and .price
 for (int i = 0; i < Lijst.Count; i++)
  {
   if (Lijst[i].scanned == false)
    {
     // (removed deletion of list item i here)
     if (Lijst[i].price > (int)nudMinimum.Value)
      {
       Totaal++;
        lblDebug.Text = Totaal.ToString();
      }
    Lijst.RemoveAt(i); //<-moved to here
    } 
  }

现在我想知道这样做的正确方法是什么,而不会使索引超出范围错误。

循环浏览列表和删除项目的正确方法

为什么不直接List<T>.RemoveAll()

https://msdn.microsoft.com/en-us/library/wdka673a(v=vs.110).aspx

在您的情况下

  Lijst.RemoveAll(item => some condition);

例如

  // Count all the not scanned items each of them exceeds nudMinimum.Value
  lblDebug.Text = Lijst
    .Where(item => !item.scanned && item.price > (int)nudMinimum.Value)
    .Count()
    .ToString();
  // Remove all not scanned items
  Lijst.RemoveAll(item => !item.scanned);

你可能正在寻找这个

for (int i = Lijst.Count - 1 ; i >= 0 ; i--)
{
    if (Lijst[i].scanned == false)
    {
        if (Lijst[i].price > (int)nudMinimum.Value)
        {
            Totaal++;
            lblDebug.Text = Totaal.ToString();
        }
        Lijst.RemoveAt(i);
    }
}

评论中的问题:

为什么循环的另一个方向会起作用?

因为当循环从零运行到计数时,会出现索引不可用且计数仍然存在的情况。例如:

如果列表中有 10 个项目,则循环从 0 开始并删除 0,1,2,3,4,现在剩下的项目是 5,索引也是 5,它也将删除该项目。之后,当循环值达到 6 并且剩余的项目为 4 时。然后它会产生一个问题。它会抛出错误。即索引超出范围

你去

// 1. Count items
lblDebug.Text = Lijst.Count(x => x.price > (int)nudMinimum.Value && !x.scanned).ToString();
//2. Remove items
Lijst.RemoveAll(x => !x.scanned);

问题是,当你删除元素编号 5 时,列表会变短,元素编号 6 现在是第 5 个,数字 7 变成了第 6 个,依此类推。但是,如果向后运行循环,则数字将按预期保留。

for(int i = donkeys.Count - 1; i >= 0; i++)
  if(donkeys[i] == some condition here)
    donkeys.RemoveAt(i);

然而,这是一种像老板一样的方法。有更好的方法。您已经有了答案,但我想建议一种基于 LINQ 的方法。

int Totaal = Lijst
  .Where(item => item.scanned)
  .Where(item => item.price > (int)nudMinimum.Value)
  .Count();
Lijst = Lijst.Where(item => !item.scanned).ToList()

另外,作为旁注,我想知道您是否发现以下内容更具可读性。请考虑以下不同的命名(关于语言和大小写)。

List<Item> items = ...;
int minimum = (int)nudMinimum.Value;
int total = items
  .Where(item => item.scanned)
  .Where(item => item.price > minimum)
  .Count();
items = items
  .Where(item => !item.scanned)
  .ToList();

首先,您要删除索引为 i 的元素,然后使用它。您需要首先使用具有索引 i 的元素执行您的过程,然后将其删除。您的代码将如下所示:

for (int i = 0; i < Lijst.Count; i++)
  {
   if (Lijst[i].scanned == false)
    {
     if (Lijst[i].price > (int)nudMinimum.Value)
      {
       Totaal++;
        lblDebug.Text = Totaal.ToString();
      }
Lijst.RemoveAt(i);
    } 
  }

通常,如果要从列表中删除与谓词匹配的所有项目,请使用 List<T>.RemoveAll() ,例如:

List<int> test = Enumerable.Range(0, 10).ToList();
test.RemoveAll(value => value%2 == 0); // Remove all even numbers.
Console.WriteLine(string.Join(", ", test));

但是,您似乎需要进行一些额外的处理。您有两种选择:

  1. 分两步完成;首先使用 RemoveAll() 删除不需要的项目,然后循环访问列表以单独处理剩余项目。
  2. 改为从List.Count-1向后循环到0

你的代码有些格式不正确。首先,您删除了列表项,然后尝试获取该已删除项的价格。怎么可能。

所以你可以这样写。

    for (int i = 0; i < Lijst.Count; i++)
  {
   if (Lijst[i].scanned == false)
    {
      if (Lijst[i].price > (int)nudMinimum.Value)
      {
       Totaal++;
        lblDebug.Text = Totaal.ToString();
      }
     Lijst.RemoveAt(i);        
    } 
  }
 List<string> list = new List<string>();
         list.Add("sasa");
         list.Add("sames");
         list.Add("samu");
         list.Add("james");
         for (int i = list.Count - 1; i >= 0; i--)
         {
             list.RemoveAt(i);
        }

如何从列表中删除项目