Best way to remove items from a collection

前端 未结 15 2020
天命终不由人
天命终不由人 2020-12-08 05:47

What is the best way to approach removing items from a collection in C#, once the item is known, but not it\'s index. This is one way to do it, but it seems inelegant at be

相关标签:
15条回答
  • 2020-12-08 06:28

    For a simple List structure the most efficient way seems to be using the Predicate RemoveAll implementation.

    Eg.

     workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);
    

    The reasons are:

    1. The Predicate/Linq RemoveAll method is implemented in List and has access to the internal array storing the actual data. It will shift the data and resize the internal array.
    2. The RemoveAt method implementation is quite slow, and will copy the entire underlying array of data into a new array. This means reverse iteration is useless for List

    If you are stuck implementing this in a the pre c# 3.0 era. You have 2 options.

    • The easily maintainable option. Copy all the matching items into a new list and and swap the underlying list.

    Eg.

    List<int> list2 = new List<int>() ; 
    foreach (int i in GetList())
    {
        if (!(i % 2 == 0))
        {
            list2.Add(i);
        }
    }
    list2 = list2;
    

    Or

    • The tricky slightly faster option, which involves shifting all the data in the list down when it does not match and then resizing the array.

    If you are removing stuff really frequently from a list, perhaps another structure like a HashTable (.net 1.1) or a Dictionary (.net 2.0) or a HashSet (.net 3.5) are better suited for this purpose.

    0 讨论(0)
  • 2020-12-08 06:29

    Similar to Dictionary Collection point of view, I have done this.

    Dictionary<string, bool> sourceDict = new Dictionary<string, bool>();
    sourceDict.Add("Sai", true);
    sourceDict.Add("Sri", false);
    sourceDict.Add("SaiSri", true);
    sourceDict.Add("SaiSriMahi", true);
    
    var itemsToDelete = sourceDict.Where(DictItem => DictItem.Value == false);
    
    foreach (var item in itemsToDelete)
    {
        sourceDict.Remove(item.Key);
    }
    

    Note: Above code will fail in .Net Client Profile (3.5 and 4.5) also some viewers mentioned it is Failing for them in .Net4.0 as well not sure which settings are causing the problem.

    So replace with below code (.ToList()) for Where statement, to avoid that error. “Collection was modified; enumeration operation may not execute.”

    var itemsToDelete = sourceDict.Where(DictItem => DictItem.Value == false).ToList();
    

    Per MSDN From .Net4.5 onwards Client Profile are discontinued. http://msdn.microsoft.com/en-us/library/cc656912(v=vs.110).aspx

    0 讨论(0)
  • 2020-12-08 06:36

    To do this while looping through the collection and not to get the modifying a collection exception, this is the approach I've taken in the past (note the .ToList() at the end of the original collection, this creates another collection in memory, then you can modify the existing collection)

    foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments.ToList())
    {
        if (spAssignment.Member.Name == shortName)
        {
            workspace.RoleAssignments.Remove(spAssignment);
        }
    }
    
    0 讨论(0)
  • 2020-12-08 06:40

    If you have got a List<T>, then List<T>.RemoveAll is your best bet. There can't be anything more efficient. Internally it does the array moving in one shot, not to mention it is O(N).

    If all you got is an IList<T> or an ICollection<T> you got roughly these three options:

        public static void RemoveAll<T>(this IList<T> ilist, Predicate<T> predicate) // O(N^2)
        {
            for (var index = ilist.Count - 1; index >= 0; index--)
            {
                var item = ilist[index];
                if (predicate(item))
                {
                    ilist.RemoveAt(index);
                }
            }
        }
    

    or

        public static void RemoveAll<T>(this ICollection<T> icollection, Predicate<T> predicate) // O(N)
        {
            var nonMatchingItems = new List<T>();
    
            // Move all the items that do not match to another collection.
            foreach (var item in icollection) 
            {
                if (!predicate(item))
                {
                    nonMatchingItems.Add(item);
                }
            }
    
            // Clear the collection and then copy back the non-matched items.
            icollection.Clear();
            foreach (var item in nonMatchingItems)
            {
                icollection.Add(item);
            }
        }
    

    or

        public static void RemoveAll<T>(this ICollection<T> icollection, Func<T, bool> predicate) // O(N^2)
        {
            foreach (var item in icollection.Where(predicate).ToList())
            {
                icollection.Remove(item);
            }
        }
    

    Go for either 1 or 2.

    1 is lighter on memory and faster if you have less deletes to perform (i.e. predicate is false most of the times).

    2 is faster if you have more deletes to perform.

    3 is the cleanest code but performs poorly IMO. Again all that depends on input data.

    For some benchmarking details see https://github.com/dotnet/BenchmarkDotNet/issues/1505

    0 讨论(0)
  • 2020-12-08 06:42

    If you want to access members of the collection by one of their properties, you might consider using a Dictionary<T> or KeyedCollection<T> instead. This way you don't have to search for the item you're looking for.

    Otherwise, you could at least do this:

    foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
    {
        if (spAssignment.Member.Name == shortName)
        {
            workspace.RoleAssignments.Remove(spAssignment);
            break;
        }
    }
    
    0 讨论(0)
  • 2020-12-08 06:42

    Save your items first, than delete them.

    var itemsToDelete = Items.Where(x => !!!your condition!!!).ToArray();
    for (int i = 0; i < itemsToDelete.Length; ++i)
        Items.Remove(itemsToDelete[i]);
    

    You need to override GetHashCode() in your Item class.

    0 讨论(0)
提交回复
热议问题