删除列表中项目的最快方法

时间:2009-08-28 21:32:00

标签: c# list collections filtering

我有一个User对象列表,我必须从列表中删除一个具有特定UserID的项目。

此方法必须尽可能快,目前我循环遍历每个项目并检查ID是否与UserID匹配,如果没有,则将该行添加到我的filteredList集合中。

List allItems = GetItems();

for(int x = 0; x < allItems.Count; x++)
{
    if(specialUserID == allItems[x].ID)
        continue;
    else
        filteredItems.Add( allItems[x] );
}

11 个答案:

答案 0 :(得分:8)

如果确实必须尽可能快,请使用不同的数据结构。列表未知删除效率。将ID映射到用户的词典怎么样?

答案 1 :(得分:4)

好吧,如果你想创建一个新的集合来保持原始的不变,你必须循环遍历所有项目。

从头开始创建具有正确容量的新列表,以最大限度地减少分配。

continue的程序逻辑似乎有点倒退......只需使用!=运算符而不是==运算符:

List<User> allItems = GetItems();

List<User> filteredItems = new List<User>(allItems.Count - 1);

foreach (User u in allItems) {
   if(u.ID != specialUserID) {
      filteredItems.Add(u);
   }
}

如果您想更改原始集合而不是创建新集合,则将项目存储在Dictionary<int, User>中将是最快的选择。定位项目并将其删除都接近O(1)操作,这样可以使整个操作接近O(1)操作而不是O(n)操作。

答案 2 :(得分:3)

使用哈希表。对于一切假设具有最小碰撞潜力的良好散列算法的查找时间是O(1)。我会推荐一些实现IDictionary的东西

答案 3 :(得分:2)

如果您必须从一个列表转移到另一个列表,这是我找到的禁食结果:

        var filtered = new List<SomeClass>(allItems);
        for (int i = 0; i < filtered.Count; i++)
            if (filtered[i].id == 9999)
                filtered.RemoveAt(i);

我尝试比较你的方法,上面的方法和linq“where”语句:

            var allItems = new List<SomeClass>();
        for (int i = 0; i < 10000000; i++)
            allItems.Add(new SomeClass() { id = i });

        Console.WriteLine("Tests Started");
        var timer = new Stopwatch();

        timer.Start();
        var filtered = new List<SomeClass>();
        foreach (var item in allItems)
            if (item.id != 9999)
                filtered.Add(item);
        var y = filtered.Last();
        timer.Stop();
        Console.WriteLine("Transfer to filtered list: {0}", timer.Elapsed.TotalMilliseconds);

        timer.Reset();
        timer.Start();
        filtered = new List<SomeClass>(allItems);
        for (int i = 0; i < filtered.Count; i++)
            if (filtered[i].id == 9999)
                filtered.RemoveAt(i);
        var s = filtered.Last();
        timer.Stop();
        Console.WriteLine("Removal from filtered list: {0}", timer.Elapsed.TotalMilliseconds);

        timer.Reset();
        timer.Start();
        var linqresults = allItems.Where(x => (x.id != 9999));
        var m = linqresults.Last();
        timer.Stop();
        Console.WriteLine("linq list: {0}", timer.Elapsed.TotalMilliseconds);

结果如下: 测试开始

转移到已过滤的清单:610.5473

从筛选列表中删除:207.5675

linq list:379.4382

使用“Add(someCollection)”并使用“.RemoveAt”的速度更快。

此外,后续.RemoveAt调用非常便宜。

答案 4 :(得分:1)

我知道它不是最快的,但是通用列表和删除()呢? (msdn)。任何人都知道它与例如相比如何表现。问题中的例子?

答案 5 :(得分:1)

这是一个想法,你怎么不删除它本身。我的意思是这样的:

public static IEnumerable<T> LoopWithExclusion<T>(this IEnumerable<T> list, Func<T,bool> excludePredicate)
{
   foreach(var item in list)
   {
      if(excludePredicate(item))
      {
         continue;
      }

      yield return item;
   }
}

关键是,只要你需要一个“过滤”列表,只需调用这个循环遍历原始列表的扩展方法,返回所有项目,除了你不想要的项目。

这样的事情:

List<User> users = GetUsers();

//later in the code when you need the filtered list:

foreach(var user in users.LoopWithExclusion(u => u.Id == myIdToExclude))
{
   //do what you gotta do
}

答案 6 :(得分:1)

假设列表的计数是偶数,我会:

  

(a)获取处理器数量列表

     

(b)将列表划分为每个处理器的相等块

     

(c)为每个具有这些数据块的处理器生成一个线程,终止条件是如果发现谓词返回一个布尔标志。

答案 7 :(得分:1)

public static void RemoveSingle<T>(this List<T> items, Predicate<T> match)
{
    int i = -1;
    while (i < items.Count && !match(items[++i])) ;
    if (i < items.Count)
    {
        items[i] = items[items.Count - 1];
        items.RemoveAt(items.Count - 1);
    }
}

答案 8 :(得分:0)

我无法理解为什么最简单,直接和明显的解决方案(也是基于List的解决方案中最快的解决方案)并非由任何人提供。 此代码删除具有匹配ID的一个项目。

for(int i = 0; i < items.Count; i++) {
    if(items[i].ID == specialUserID) {
        items.RemoveAt[i];
        break;
    }
}

答案 9 :(得分:0)

如果您有一个列表,并且您希望在适当的位置进行变异以删除符合条件的项目,则以下内容比目前发布的任何备选项目更快:

        for (int i = allItems.Count - 1; i >= 0; i--)
            if (allItems[i].id == 9999)
                allItems.RemoveAt(i);

对于某些用途,Dictionary可能会更快,但不要对List进行折扣。对于小型集合,它可能会更快,对于大型集合,它可以节省内存,这可能反过来使您的应用程序整体更快。分析是确定实际应用程序中哪个更快的唯一方法。

答案 10 :(得分:-1)

如果您有数百或数千个项目,以下是一些有效的代码:

List allItems = GetItems();
//Choose the correct loop here

if((x % 5) == 0 && (X >= 5))
{
     for(int x = 0; x < allItems.Count; x = x + 5)
     {
         if(specialUserID != allItems[x].ID)
             filteredItems.Add( allItems[x] );
         if(specialUserID != allItems[x+1].ID)
             filteredItems.Add( allItems[x+1] );
         if(specialUserID != allItems[x+2].ID)
             filteredItems.Add( allItems[x+2] );
         if(specialUserID != allItems[x+3].ID)
             filteredItems.Add( allItems[x+3] );
         if(specialUserID != allItems[x+4].ID)
             filteredItems.Add( allItems[x+4] );
      }
 }

如果循环的大小可以被最大数字整除到最小数字,则开始测试。如果你想在循环中使用10个if语句,那么测试列表的大小是否大于10并且可以被10整除然后从那里向下。例如,如果你有99个项目---你可以在循环中使用9个if语句。循环将迭代11次而不是99次

“if”语句便宜且快速