How to remove elements from a generic list while iterating over it?

前端 未结 27 2372
忘了有多久
忘了有多久 2020-11-21 22:48

I am looking for a better pattern for working with a list of elements which each need processed and then depending on the outcome are removed from

相关标签:
27条回答
  • 2020-11-21 23:36

    Using the ToArray() on a generic list allows you to do a Remove(item) on your generic List:

            List<String> strings = new List<string>() { "a", "b", "c", "d" };
            foreach (string s in strings.ToArray())
            {
                if (s == "b")
                    strings.Remove(s);
            }
    
    0 讨论(0)
  • 2020-11-21 23:36

    Using Remove or RemoveAt on a list while iterating over that list has intentionally been made difficult, because it is almost always the wrong thing to do. You might be able to get it working with some clever trick, but it would be extremely slow. Every time you call Remove it has to scan through the entire list to find the element you want to remove. Every time you call RemoveAt it has to move subsequent elements 1 position to the left. As such, any solution using Remove or RemoveAt, would require quadratic time, O(n²).

    Use RemoveAll if you can. Otherwise, the following pattern will filter the list in-place in linear time, O(n).

    // Create a list to be filtered
    IList<int> elements = new List<int>(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
    // Filter the list
    int kept = 0;
    for (int i = 0; i < elements.Count; i++) {
        // Test whether this is an element that we want to keep.
        if (elements[i] % 3 > 0) {
            // Add it to the list of kept elements.
            elements[kept] = elements[i];
            kept++;
        }
    }
    // Unfortunately IList has no Resize method. So instead we
    // remove the last element of the list until: elements.Count == kept.
    while (kept < elements.Count) elements.RemoveAt(elements.Count-1);
    
    0 讨论(0)
  • 2020-11-21 23:38

    My approach is that I first create a list of indices, which should get deleted. Afterwards I loop over the indices and remove the items from the initial list. This looks like this:

    var messageList = ...;
    // Restrict your list to certain criteria
    var customMessageList = messageList.FindAll(m => m.UserId == someId);
    
    if (customMessageList != null && customMessageList.Count > 0)
    {
        // Create list with positions in origin list
        List<int> positionList = new List<int>();
        foreach (var message in customMessageList)
        {
            var position = messageList.FindIndex(m => m.MessageId == message.MessageId);
            if (position != -1)
                positionList.Add(position);
        }
        // To be able to remove the items in the origin list, we do it backwards
        // so that the order of indices stays the same
        positionList = positionList.OrderByDescending(p => p).ToList();
        foreach (var position in positionList)
        {
            messageList.RemoveAt(position);
        }
    }
    
    0 讨论(0)
提交回复
热议问题