简体   繁体   English

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

[英]fastest way to remove an item in a list

I have a list of User objects, and I have to remove ONE item from the list with a specific UserID. 我有一个User对象的列表,我必须从列表中删除一个具有特定UserID的项目。

This method has to be as fast as possible, currently I am looping through each item and checking if the ID matches the UserID, if not, then I add the row to a my filteredList collection. 此方法必须尽可能快,目前,我正在遍历每个项目,并检查ID是否与UserID匹配,如果不匹配,则将行添加到我的filterList集合中。

List allItems = GetItems();

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

If it really has to be as fast as possible, use a different data structure. 如果确实必须尽可能快,请使用其他数据结构。 List isn't known for efficiency of deletion. List的删除效率未知。 How about a Dictionary that maps ID to User? 将ID映射到User的字典怎么样?

Well, if you want to create a new collection to leave the original untouched, you have to loop through all the items. 好吧,如果您想创建一个新集合以保持原始状态不变,则必须遍历所有项目。

Create the new list with the right capacity from the start, that minimises allocations. 从一开始就以合适的容量创建新列表,以最大程度地减少分配。

Your program logic with the continue seems a bit backwards... just use the != operator instead of the == operator: 您的带有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);
   }
}

If you want to change the original collection instead of creating a new, storing the items in a Dictionary<int, User> would be the fastest option. 如果要更改原始集合而不是创建新集合,则将项目存储在Dictionary<int, User>中将是最快的选择。 Both locating the item and removing it are close to O(1) operations, so that would make the whole operation close to an O(1) operation instead of an O(n) operation. 定位和删除项目都接近O(1)操作,因此会使整个操作接近O(1)操作而不是O(n)操作。

Use a hashtable. 使用哈希表。 Lookup time is O(1) for everything assuming a good hash algorithm with minimal collision potential. 假设具有良好的散列算法且冲突可能性最小,则所有内容的查找时间均为O(1)。 I would recommend something that implements IDictionary 我会推荐一些实现IDictionary的东西

If you must transfer from one list to another here is the fasted result I've found: 如果您必须从一个列表转移到另一个列表,那么这里是我发现的禁食结果:

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

I tried comparing your method, the method above, and a linq "where" statement: 我尝试比较您的方法,上述方法和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);

The results were as follows: Tests Started 结果如下:测试开始

Transfer to filtered list: 610.5473 转移到过滤列表:610.5473

Removal from filtered list: 207.5675 从筛选清单中移除:207.5675

linq list: 379.4382 linq列表:379.4382

using the "Add(someCollection)" and using a ".RemoveAt" was a good deal faster. 使用“ Add(someCollection)”并使用“ .RemoveAt”要快得多。

Also, subsequent .RemoveAt calls are pretty cheap. 此外,后续的.RemoveAt调用非常便宜。

I know it's not the fastest, but what about generic list and remove()? 我知道它不是最快的,但是泛型列表和remove()呢? ( msdn ). msdn )。 Anybody knows how it performs compared to eg. 与例如相比,任何人都知道它的性能。 the example in the question? 问题中的示例?

Here's a thought, how about you don't remove it per se. 这是一个想法,您如何不删除它本身。 What I mean is something like this: 我的意思是这样的:

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;
   }
}

The point being, whenever you need a "filtered" list, just call this extension method, which loops through the original list, returns all of the items, EXCEPT the ones you don't want. 关键是,每当需要“过滤的”列表时,只需调用此扩展方法即可,该方法循环遍历原始列表,返回所有项目,但不包含不需要的项目。

Something like this: 像这样:

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
}

Assuming the count of the list is even, I would : 假设列表的数量是偶数,我将:

(a) get a list of the number of processors (a)获取处理器数量的列表

(b) Divide your list into equal chunks for each processors (b)将每个处理器的列表分成相等的块

(c) spawn a thread for each processor with these data chunks, with the terminating condition being if the predicate is found to return a boolean flag. (c)为每个带有这些数据块的处理器生成一个线程,终止条件是是否发现谓词返回布尔标志。

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

I cannot understand why the most easy, straight-forward and obvious solution (also the fastest among the List -based ones) wasn't given by anyone. 我不明白为什么没有任何人提供最简单,直接和明显的解决方案(也是基于List的解决方案中最快的)。 This code removes ONE item with a matching ID. 此代码删除一个具有匹配ID的项目。

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

If you have a list and you want to mutate it in place to remove an item matching a condition the following is faster than any of the alternatives posted so far: 如果您有一个列表,并且想要对其进行适当的突变以删除一个符合条件的项目,则以下内容比到目前为止发布的任何替代方法都快:

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

A Dictionary may be faster for some uses, but don't discount a List . Dictionary在某些用途上可能会更快,但是请不要对List进行折扣。 For small collections, it will likely be faster and for large collections, it may save memory which may, in turn make you application faster overall. 对于小型集合,它可能会更快,而对于大型集合,它可以节省内存,从而可能反过来使您的应用程序总体上更快。 Profiling is the only way to determine which is faster in a real application. 分析是确定在实际应用中哪个更快的唯一方法。

Here is some code that is efficient if you have hundreds or thousands of items: 如果您有成百上千个项目,下面是一些有效的代码:

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] );
      }
 }

Start testing if the size of the loop is divisible by the largest number to the smallest number. 开始测试循环的大小是否可被最大数除以最小数。 if you want 10 if statements in the loop then test if the size of the list is bigger then ten and divisible by ten then go down from there. 如果要在循环中使用10条if语句,则测试列表的大小是否大于10,然后将其整除10,然后从此处向下移动。 For example if you have 99 items --- you can use 9 if statements in the loop. 例如,如果您有99个项目---您可以在循环中使用9个if语句。 The loop will iterate 11 times instead of 99 times 循环将迭代11次而不是99次

"if" statements are cheap and fast “如果”语句便宜又快速

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM