如何在迭代通用列表时从通用列表中删除元素?

发布于 2024-08-07 14:31:32 字数 372 浏览 4 评论 0原文

我正在寻找一种更好的模式来处理每个需要处理的元素列表,然后根据结果从列表中删除。

您不能在 foreach (X 中的 var element) 中使用 .Remove(element) (因为它会导致 Collection 被修改;枚举操作可能无法执行. 例外)...您也不能使用 for (int i = 0; i < elements.Count(); i++).RemoveAt(i) 因为它会扰乱您当前在集合中相对于 i 的位置。

有没有一种优雅的方法来做到这一点?

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 the list.

You can't use .Remove(element) inside a foreach (var element in X) (because it results in Collection was modified; enumeration operation may not execute. exception)... you also can't use for (int i = 0; i < elements.Count(); i++) and .RemoveAt(i) because it disrupts your current position in the collection relative to i.

Is there an elegant way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(28

夕嗳→ 2024-08-14 14:31:32

使用 for 循环反向迭代列表:

for (int i = safePendingList.Count - 1; i >= 0; i--)
{
    // some code
    // safePendingList.RemoveAt(i);
}

示例:

var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
    if (list[i] > 5)
        list.RemoveAt(i);
}
list.ForEach(i => Console.WriteLine(i));

或者,您可以使用 RemoveAll方法 带有要测试的谓词:

safePendingList.RemoveAll(item => item.Value == someValue);

这是一个演示的简化示例:

var list = new List<int>(Enumerable.Range(1, 10));
Console.WriteLine("Before:");
list.ForEach(i => Console.WriteLine(i));
list.RemoveAll(i => i > 5);
Console.WriteLine("After:");
list.ForEach(i => Console.WriteLine(i));

Iterate your list in reverse with a for loop:

for (int i = safePendingList.Count - 1; i >= 0; i--)
{
    // some code
    // safePendingList.RemoveAt(i);
}

Example:

var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
    if (list[i] > 5)
        list.RemoveAt(i);
}
list.ForEach(i => Console.WriteLine(i));

Alternately, you can use the RemoveAll method with a predicate to test against:

safePendingList.RemoveAll(item => item.Value == someValue);

Here's a simplified example to demonstrate:

var list = new List<int>(Enumerable.Range(1, 10));
Console.WriteLine("Before:");
list.ForEach(i => Console.WriteLine(i));
list.RemoveAll(i => i > 5);
Console.WriteLine("After:");
list.ForEach(i => Console.WriteLine(i));
躲猫猫 2024-08-14 14:31:32
 foreach (var item in list.ToList()) {
     list.Remove(item);
 }

如果您将 .ToList() 添加到列表(或 LINQ 查询的结果),则可以直接从 中删除 item list 没有可怕的 Collection 被修改;枚举操作可能无法执行。 错误。编译器会生成 list 的副本,以便您可以安全地对数组进行删除。

虽然这种模式不是非常有效,但它具有自然的感觉,并且足够灵活,适合几乎任何情况。例如,当您希望将每个item保存到数据库中,并仅在数据库保存成功时将其从list中删除。

 foreach (var item in list.ToList()) {
     list.Remove(item);
 }

If you add .ToList() to your list (or the results of a LINQ query), you can remove item directly from list without the dreaded Collection was modified; enumeration operation may not execute. error. The compiler makes a copy of list, so that you can safely do the remove on the array.

While this pattern is not super efficient, it has a natural feel and is flexible enough for almost any situation. Such as when you want to save each item to a DB and remove it from the list only when the DB save succeeds.

深白境迁sunset 2024-08-14 14:31:32

一个简单直接的解决方案:

使用在集合上向后运行的标准 for 循环和 RemoveAt(i) 来删除元素。

A simple and straightforward solution:

Use a standard for-loop running backwards on your collection and RemoveAt(i) to remove elements.

烟沫凡尘 2024-08-14 14:31:32

当您想在迭代集合时从集合中删除元素时,首先想到的应该是反向迭代。

幸运的是,有一个比编写 for 循环更优雅的解决方案,因为 for 循环涉及不必要的键入并且容易出错。

ICollection<int> test = new List<int>(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

foreach (int myInt in test.Reverse<int>())
{
    if (myInt % 2 == 0)
    {
        test.Remove(myInt);
    }
}

Reverse iteration should be the first thing to come to mind when you want to remove elements from a Collection while iterating over it.

Luckily, there is a more elegant solution than writing a for loop which involves needless typing and can be error prone.

ICollection<int> test = new List<int>(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

foreach (int myInt in test.Reverse<int>())
{
    if (myInt % 2 == 0)
    {
        test.Remove(myInt);
    }
}
以歌曲疗慰 2024-08-14 14:31:32

选择您想要想要的元素,而不是尝试删除您不想要想要的元素。这比删除元素要容易得多(通常也更有效)。

var newSequence = (from el in list
                   where el.Something || el.AnotherThing < 0
                   select el);

我想将此作为评论发布,以回应下面迈克尔·狄龙留下的评论,但它太长了,而且无论如何在我的回答中可能很有用:

就我个人而言,我永远不会一一删除项目,如果你这样做的话需要删除,然后调用 RemoveAll ,它采用谓词并且仅重新排列内部数组一次,而 Remove 对每个元素执行 Array.Copy 操作你删除。 RemoveAll 效率大大提高。

当您向后迭代列表时,您已经有了要删除的元素的索引,因此调用 RemoveAt 会更有效,因为 Remove 首先遍历列表以找到您要删除的元素的索引,但您已经知道该索引。

总而言之,我认为没有任何理由在 for 循环中调用 Remove 。理想情况下,如果可能的话,使用上面的代码根据需要从列表中传输元素,这样就根本不需要创建第二个数据结构。

Select the elements you do want rather than trying to remove the elements you don't want. This is so much easier (and generally more efficient too) than removing elements.

var newSequence = (from el in list
                   where el.Something || el.AnotherThing < 0
                   select el);

I wanted to post this as a comment in response to the comment left by Michael Dillon below, but it's too long and probably useful to have in my answer anyway:

Personally, I'd never remove items one-by-one, if you do need removal, then call RemoveAll which takes a predicate and only rearranges the internal array once, whereas Remove does an Array.Copy operation for every element you remove. RemoveAll is vastly more efficient.

And when you're backwards iterating over a list, you already have the index of the element you want to remove, so it would be far more efficient to call RemoveAt, because Remove first does a traversal of the list to find the index of the element you're trying to remove, but you already know that index.

So all in all, I don't see any reason to ever call Remove in a for-loop. And ideally, if it is at all possible, use the above code to stream elements from the list as needed so no second data structure has to be created at all.

雨落□心尘 2024-08-14 14:31:32

在通用列表上使用 ToArray() 允许您在通用列表上执行删除(项目):

        List<String> strings = new List<string>() { "a", "b", "c", "d" };
        foreach (string s in strings.ToArray())
        {
            if (s == "b")
                strings.Remove(s);
        }

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);
        }
拥抱影子 2024-08-14 14:31:32

使用 .ToList() 将复制您的列表,如本问题所述:
ToList()--它会创建一个新列表吗?

通过使用ToList(),您可以从原始列表中删除,因为您实际上是在迭代副本。

foreach (var item in listTracked.ToList()) {    

        if (DetermineIfRequiresRemoval(item)) {
            listTracked.Remove(item)
        }

     }

Using .ToList() will make a copy of your list, as explained in this question:
ToList()-- Does it Create a New List?

By using ToList(), you can remove from your original list, because you're actually iterating over a copy.

foreach (var item in listTracked.ToList()) {    

        if (DetermineIfRequiresRemoval(item)) {
            listTracked.Remove(item)
        }

     }
沙沙粒小 2024-08-14 14:31:32

如果确定要删除哪些项目的函数没有副作用并且不会改变项目(它是一个纯函数),一个简单而有效(线性时间)的解决方案是:

list.RemoveAll(condition);

如果有副作用,我会使用类似的东西:

var toRemove = new HashSet<T>();
foreach(var item in items)
{
     ...
     if(condition)
          toRemove.Add(item);
}
items.RemoveAll(toRemove.Contains);

假设哈希值良好,这仍然是线性时间。但由于哈希集,它增加了内存使用量。

最后,如果您的列表只是一个 IList 而不是 List 我建议我的答案 我该如何做这个特殊的 foreach 迭代器?。与许多其他答案的二次运行时相比,在给定 IList 的典型实现的情况下,这将具有线性运行时。

If the function that determines which items to delete has no side effects and doesn't mutate the item (it's a pure function), a simple and efficient (linear time) solution is:

list.RemoveAll(condition);

If there are side effects, I'd use something like:

var toRemove = new HashSet<T>();
foreach(var item in items)
{
     ...
     if(condition)
          toRemove.Add(item);
}
items.RemoveAll(toRemove.Contains);

This is still linear time, assuming the hash is good. But it has an increased memory use due to the hashset.

Finally if your list is only an IList<T> instead of a List<T> I suggest my answer to How can I do this special foreach iterator?. This will have linear runtime given typical implementations of IList<T>, compared with quadratic runtime of many other answers.

蓝天白云 2024-08-14 14:31:32

由于任何删除都是在您可以使用的条件下进行的

list.RemoveAll(item => item.Value == someValue);

As any remove is taken on a condition you can use

list.RemoveAll(item => item.Value == someValue);
梦魇绽荼蘼 2024-08-14 14:31:32

您不能使用 foreach,但可以在删除项目时向前迭代并管理循环索引变量,如下所示:

for (int i = 0; i < elements.Count; i++)
{
    if (<condition>)
    {
        // Decrement the loop counter to iterate this index again, since later elements will get moved down during the remove operation.
        elements.RemoveAt(i--);
    }
}

请注意,通常所有这些技术都依赖于被迭代的集合的行为。此处显示的技术适用于标准 List(T)。 (很有可能编写自己的集合类和迭代器,确实允许在 foreach 循环期间删除项目。)

You can't use foreach, but you could iterate forwards and manage your loop index variable when you remove an item, like so:

for (int i = 0; i < elements.Count; i++)
{
    if (<condition>)
    {
        // Decrement the loop counter to iterate this index again, since later elements will get moved down during the remove operation.
        elements.RemoveAt(i--);
    }
}

Note that in general all of these techniques rely on the behaviour of the collection being iterated. The technique shown here will work with the standard List(T). (It is quite possible to write your own collection class and iterator that does allow item removal during a foreach loop.)

静谧幽蓝 2024-08-14 14:31:32
List<T> TheList = new List<T>();

TheList.FindAll(element => element.Satisfies(Condition)).ForEach(element => TheList.Remove(element));
List<T> TheList = new List<T>();

TheList.FindAll(element => element.Satisfies(Condition)).ForEach(element => TheList.Remove(element));
天邊彩虹 2024-08-14 14:31:32

For 循环对此来说是一个糟糕的构造。

使用 while

var numbers = new List<int>(Enumerable.Range(1, 3));

while (numbers.Count > 0)
{
    numbers.RemoveAt(0);
}

但是,如果您绝对必须使用 for

var numbers = new List<int>(Enumerable.Range(1, 3));

for (; numbers.Count > 0;)
{
    numbers.RemoveAt(0);
}

或者,这样:

public static class Extensions
{

    public static IList<T> Remove<T>(
        this IList<T> numbers,
        Func<T, bool> predicate)
    {
        numbers.ForEachBackwards(predicate, (n, index) => numbers.RemoveAt(index));
        return numbers;
    }

    public static void ForEachBackwards<T>(
        this IList<T> numbers,
        Func<T, bool> predicate,
        Action<T, int> action)
    {
        for (var i = numbers.Count - 1; i >= 0; i--)
        {
            if (predicate(numbers[i]))
            {
                action(numbers[i], i);
            }
        }
    }
}

用法:

var numbers = new List<int>(Enumerable.Range(1, 10)).Remove((n) => n > 5);

但是,LINQ 已经有 RemoveAll() 来执行此操作

var numbers = new List<int>(Enumerable.Range(1, 10));
numbers.RemoveAll((n) => n > 5);

最后,您可能最好使用 LINQ 的 Where() 来过滤和创建新列表,而不是改变现有列表。不变性通常是好的。

var numbers = new List<int>(Enumerable.Range(1, 10))
    .Where((n) => n <= 5)
    .ToList();

For loops are a bad construct for this.

Using while

var numbers = new List<int>(Enumerable.Range(1, 3));

while (numbers.Count > 0)
{
    numbers.RemoveAt(0);
}

But, if you absolutely must use for

var numbers = new List<int>(Enumerable.Range(1, 3));

for (; numbers.Count > 0;)
{
    numbers.RemoveAt(0);
}

Or, this:

public static class Extensions
{

    public static IList<T> Remove<T>(
        this IList<T> numbers,
        Func<T, bool> predicate)
    {
        numbers.ForEachBackwards(predicate, (n, index) => numbers.RemoveAt(index));
        return numbers;
    }

    public static void ForEachBackwards<T>(
        this IList<T> numbers,
        Func<T, bool> predicate,
        Action<T, int> action)
    {
        for (var i = numbers.Count - 1; i >= 0; i--)
        {
            if (predicate(numbers[i]))
            {
                action(numbers[i], i);
            }
        }
    }
}

Usage:

var numbers = new List<int>(Enumerable.Range(1, 10)).Remove((n) => n > 5);

However, LINQ already has RemoveAll() to do this

var numbers = new List<int>(Enumerable.Range(1, 10));
numbers.RemoveAll((n) => n > 5);

Lastly, you are probably better off using LINQ's Where() to filter and create a new list instead of mutating the existing list. Immutability is usually good.

var numbers = new List<int>(Enumerable.Range(1, 10))
    .Where((n) => n <= 5)
    .ToList();
别低头,皇冠会掉 2024-08-14 14:31:32

在迭代列表时在列表上使用 RemoveRemoveAt 故意变得困难,因为它几乎总是错误的做法。你也许可以通过一些巧妙的技巧让它工作,但速度会非常慢。每次调用Remove时,它都必须扫描整个列表以找到要删除的元素。每次调用 RemoveAt 时,它都必须将后续元素向左移动 1 个位置。因此,任何使用 RemoveRemoveAt 的解决方案都需要二次时间,O(n²)

如果可以的话,使用RemoveAll。否则,以下模式将在线性时间内就地过滤列表,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);

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);
影子是时光的心 2024-08-14 14:31:32

在迭代列表时从列表中删除项目的最佳方法是使用 RemoveAll()。但人们主要担心的是他们必须在循环内做一些复杂的事情和/或有复杂的比较情况。

解决方案是仍然使用 RemoveAll() 但使用以下表示法:

var list = new List<int>(Enumerable.Range(1, 10));
list.RemoveAll(item => 
{
    // Do some complex operations here
    // Or even some operations on the items
    SomeFunction(item);
    // In the end return true if the item is to be removed. False otherwise
    return item > 5;
});

The best way to remove items from a list while iterating over it is to use RemoveAll(). But the main concern written by people is that they have to do some complex things inside the loop and/or have complex compare cases.

The solution is to still use RemoveAll() but use this notation:

var list = new List<int>(Enumerable.Range(1, 10));
list.RemoveAll(item => 
{
    // Do some complex operations here
    // Or even some operations on the items
    SomeFunction(item);
    // In the end return true if the item is to be removed. False otherwise
    return item > 5;
});
泪痕残 2024-08-14 14:31:32

我会从 LINQ 查询中重新分配列表,该查询会过滤掉您不想保留的元素。

list = list.Where(item => ...).ToList();

除非列表非常大,否则执行此操作不会出现明显的性能问题。

I would reassign the list from a LINQ query that filtered out the elements you didn't want to keep.

list = list.Where(item => ...).ToList();

Unless the list is very large there should be no significant performance problems in doing this.

一直在等你来 2024-08-14 14:31:32

假设谓词是元素的布尔属性,如果它为 true,则应删除该元素:

        int i = 0;
        while (i < list.Count())
        {
            if (list[i].predicate == true)
            {
                list.RemoveAt(i);
                continue;
            }
            i++;
        }

By assuming that predicate is a Boolean property of an element, that if it is true, then the element should be removed:

        int i = 0;
        while (i < list.Count())
        {
            if (list[i].predicate == true)
            {
                list.RemoveAt(i);
                continue;
            }
            i++;
        }
离不开的别离 2024-08-14 14:31:32

在 C# 中,一种简单的方法是标记要删除的列表,然后创建一个新列表进行迭代...

foreach(var item in list.ToList()){if(item.Delete) list.Remove(item);}  

或者更简单地使用 linq...

list.RemoveAll(p=>p.Delete);

但值得考虑其他任务或线程是否可以访问相同的列表在您忙于删除的同时列出,也许可以使用 ConcurrentList 代替。

In C# one easy way is to mark the ones you wish to delete then create a new list to iterate over...

foreach(var item in list.ToList()){if(item.Delete) list.Remove(item);}  

or even simpler use linq....

list.RemoveAll(p=>p.Delete);

but it is worth considering if other tasks or threads will have access to the same list at the same time you are busy removing, and maybe use a ConcurrentList instead.

想念有你 2024-08-14 14:31:32

有一个选项这里没有提到。

如果您不介意在项目中的某处添加一些代码,则可以添加和扩展 List 以返回反向迭代列表的类的实例。

您可以像这样使用它:

foreach (var elem in list.AsReverse())
{
    //Do stuff with elem
    //list.Remove(elem); //Delete it if you want
}

下面是扩展的样子:

public static class ReverseListExtension
{
    public static ReverseList<T> AsReverse<T>(this List<T> list) => new ReverseList<T>(list);

    public class ReverseList<T> : IEnumerable
    {
        List<T> list;
        public ReverseList(List<T> list){ this.list = list; }

        public IEnumerator GetEnumerator()
        {
            for (int i = list.Count - 1; i >= 0; i--)
                yield return list[i];
            yield break;
        }
    }
}

这基本上是没有分配的 list.Reverse() 。

就像一些人提到的那样,您仍然会遇到逐个删除元素的缺点,如果您的列表非常长,那么这里的一些选项会更好。但我认为有人会想要 list.Reverse() 的简单性,而不需要内存开销。

There is an option that hasn't been mentioned here.

If you don't mind adding a bit of code somewhere in your project, you can add and extension to List to return an instance of a class that does iterate through the list in reverse.

You would use it like this :

foreach (var elem in list.AsReverse())
{
    //Do stuff with elem
    //list.Remove(elem); //Delete it if you want
}

And here is what the extension looks like:

public static class ReverseListExtension
{
    public static ReverseList<T> AsReverse<T>(this List<T> list) => new ReverseList<T>(list);

    public class ReverseList<T> : IEnumerable
    {
        List<T> list;
        public ReverseList(List<T> list){ this.list = list; }

        public IEnumerator GetEnumerator()
        {
            for (int i = list.Count - 1; i >= 0; i--)
                yield return list[i];
            yield break;
        }
    }
}

This is basically list.Reverse() without the allocation.

Like some have mentioned you still get the drawback of deleting elements one by one, and if your list is massively long some of the options here are better. But I think there is a world where someone would want the simplicity of list.Reverse(), without the memory overhead.

神妖 2024-08-14 14:31:32

希望“模式”是这样的:

foreach( thing in thingpile )
{
    if( /* condition#1 */ )
    {
        foreach.markfordeleting( thing );
    }
    elseif( /* condition#2 */ )
    {
        foreach.markforkeeping( thing );
    }
} 
foreachcompleted
{
    // then the programmer's choices would be:

    // delete everything that was marked for deleting
    foreach.deletenow(thingpile); 

    // ...or... keep only things that were marked for keeping
    foreach.keepnow(thingpile);

    // ...or even... make a new list of the unmarked items
    others = foreach.unmarked(thingpile);   
}

这将使代码与程序员大脑中进行的过程保持一致。

I wish the "pattern" was something like this:

foreach( thing in thingpile )
{
    if( /* condition#1 */ )
    {
        foreach.markfordeleting( thing );
    }
    elseif( /* condition#2 */ )
    {
        foreach.markforkeeping( thing );
    }
} 
foreachcompleted
{
    // then the programmer's choices would be:

    // delete everything that was marked for deleting
    foreach.deletenow(thingpile); 

    // ...or... keep only things that were marked for keeping
    foreach.keepnow(thingpile);

    // ...or even... make a new list of the unmarked items
    others = foreach.unmarked(thingpile);   
}

This would align the code with the process that goes on in the programmer's brain.

開玄 2024-08-14 14:31:32
foreach(var item in list.ToList())

{

if(item.Delete) list.Remove(item);

}

只需从第一个列表创建一个全新的列表即可。我说“简单”而不是“正确”,因为创建一个全新的列表可能比以前的方法具有更高的性能(我没有费心进行任何基准测试。)我通常更喜欢这种模式,它在克服问题方面也很有用Linq-To-Entity 限制。

for(i = list.Count()-1;i>=0;i--)

{

item=list[i];

if (item.Delete) list.Remove(item);

}

这种方式使用普通的旧 For 循环向后循环列表。如果集合的大小发生变化,向前执行此操作可能会出现问题,但向后执行应该始终是安全的。

foreach(var item in list.ToList())

{

if(item.Delete) list.Remove(item);

}

Simply create an entirely new list from the first one. I say "Easy" rather than "Right" as creating an entirely new list probably comes at a performance premium over the previous method (I haven't bothered with any benchmarking.) I generally prefer this pattern, it can also be useful in overcoming Linq-To-Entities limitations.

for(i = list.Count()-1;i>=0;i--)

{

item=list[i];

if (item.Delete) list.Remove(item);

}

This way cycles through the list backwards with a plain old For loop. Doing this forwards could be problematic if the size of the collection changes, but backwards should always be safe.

单调的奢华 2024-08-14 14:31:32

只是想添加我的 2 美分,以防这对任何人有帮助,我遇到了类似的问题,但需要在迭代时从数组列表中删除多个元素。最高票数的答案在很大程度上为我做到了这一点,直到我遇到错误并意识到在某些情况下索引大于数组列表的大小,因为多个元素被删除但循环的索引没有保留的踪迹。我通过一个简单的检查修复了这个问题:

ArrayList place_holder = new ArrayList();
place_holder.Add("1");
place_holder.Add("2");
place_holder.Add("3");
place_holder.Add("4");

for(int i = place_holder.Count-1; i>= 0; i--){
    if(i>= place_holder.Count){
        i = place_holder.Count-1; 
    }

// some method that removes multiple elements here
}

Just wanted to add my 2 cents to this in case this helps anyone, I had a similar problem but needed to remove multiple elements from an array list while it was being iterated over. the highest upvoted answer did it for me for the most part until I ran into errors and realized that the index was greater than the size of the array list in some instances because multiple elements were being removed but the index of the loop didn't keep track of that. I fixed this with a simple check:

ArrayList place_holder = new ArrayList();
place_holder.Add("1");
place_holder.Add("2");
place_holder.Add("3");
place_holder.Add("4");

for(int i = place_holder.Count-1; i>= 0; i--){
    if(i>= place_holder.Count){
        i = place_holder.Count-1; 
    }

// some method that removes multiple elements here
}
×纯※雪 2024-08-14 14:31:32

复制您正在迭代的列表。然后从副本中取出并与原件进行交互。向后运行会令人困惑,并且在并行循环时效果不佳。

var ids = new List<int> { 1, 2, 3, 4 };
var iterableIds = ids.ToList();

Parallel.ForEach(iterableIds, id =>
{
    ids.Remove(id);
});

Copy the list you are iterating. Then remove from the copy and interate the original. Going backwards is confusing and doesn't work well when looping in parallel.

var ids = new List<int> { 1, 2, 3, 4 };
var iterableIds = ids.ToList();

Parallel.ForEach(iterableIds, id =>
{
    ids.Remove(id);
});
冧九 2024-08-14 14:31:32

我想要这个

using System.IO;
using System;
using System.Collections.Generic;

class Author
    {
        public string Firstname;
        public string Lastname;
        public int no;
    }

class Program
{
    private static bool isEven(int i) 
    { 
        return ((i % 2) == 0); 
    } 

    static void Main()
    {    
        var authorsList = new List<Author>()
        {
            new Author{ Firstname = "Bob", Lastname = "Smith", no = 2 },
            new Author{ Firstname = "Fred", Lastname = "Jones", no = 3 },
            new Author{ Firstname = "Brian", Lastname = "Brains", no = 4 },
            new Author{ Firstname = "Billy", Lastname = "TheKid", no = 1 }
        };

        authorsList.RemoveAll(item => isEven(item.no));

        foreach(var auth in authorsList)
        {
            Console.WriteLine(auth.Firstname + " " + auth.Lastname);
        }
    }
}

输出

Fred Jones
Billy TheKid

I would do like this

using System.IO;
using System;
using System.Collections.Generic;

class Author
    {
        public string Firstname;
        public string Lastname;
        public int no;
    }

class Program
{
    private static bool isEven(int i) 
    { 
        return ((i % 2) == 0); 
    } 

    static void Main()
    {    
        var authorsList = new List<Author>()
        {
            new Author{ Firstname = "Bob", Lastname = "Smith", no = 2 },
            new Author{ Firstname = "Fred", Lastname = "Jones", no = 3 },
            new Author{ Firstname = "Brian", Lastname = "Brains", no = 4 },
            new Author{ Firstname = "Billy", Lastname = "TheKid", no = 1 }
        };

        authorsList.RemoveAll(item => isEven(item.no));

        foreach(var auth in authorsList)
        {
            Console.WriteLine(auth.Firstname + " " + auth.Lastname);
        }
    }
}

OUTPUT

Fred Jones
Billy TheKid
星星的軌跡 2024-08-14 14:31:32

我发现自己处于类似的情况,我必须删除给定 List 中的每个第 nth 元素。

for (int i = 0, j = 0, n = 3; i < list.Count; i++)
{
    if ((j + 1) % n == 0) //Check current iteration is at the nth interval
    {
        list.RemoveAt(i);
        j++; //This extra addition is necessary. Without it j will wrap
             //down to zero, which will throw off our index.
    }
    j++; //This will always advance the j counter
}

I found myself in a similar situation where I had to remove every nth element in a given List<T>.

for (int i = 0, j = 0, n = 3; i < list.Count; i++)
{
    if ((j + 1) % n == 0) //Check current iteration is at the nth interval
    {
        list.RemoveAt(i);
        j++; //This extra addition is necessary. Without it j will wrap
             //down to zero, which will throw off our index.
    }
    j++; //This will always advance the j counter
}
鱼窥荷 2024-08-14 14:31:32

从列表中删除一项的成本与要删除的项之后的项数成正比。在前一半项目符合删除条件的情况下,任何基于单独删除项目的方法最终都必须执行大约 N*N/4 项目复制操作,如果列表很大,这可能会变得非常昂贵。

更快的方法是扫描列表以找到第一个要删除的项目(如果有),然后从该点开始将应保留的每个项目复制到其所属的位置。完成此操作后,如果应保留 R 个项目,则列表中的前 R 个项目将是这 R 个项目,所有需要删除的项目将位于末尾。如果以相反的顺序删除这些项目,系统最终将不必复制它们中的任何一个,因此,如果列表有 N 个项目,其中 R 个项目(包括所有前 F 个项目)被保留,
需要复制 RF 项,并将列表缩小 NR 倍一项。所有线性时间。

The cost of removing an item from the list is proportional to the number of items following the one to be removed. In the case where the first half of the items qualify for removal, any approach which is based upon removing items individually will end up having to perform about N*N/4 item-copy operations, which can get very expensive if the list is large.

A faster approach is to scan through the list to find the first item to be removed (if any), and then from that point forward copy each item which should be retained to the spot where it belongs. Once this is done, if R items should be retained, the first R items in the list will be those R items, and all of the items requiring deletion will be at the end. If those items are deleted in reverse order, the system won't end up having to copy any of them, so if the list had N items of which R items, including all of the first F, were retained,
it will be necessary to copy R-F items, and shrink the list by one item N-R times. All linear time.

谁的年少不轻狂 2024-08-14 14:31:32

我的方法是首先创建一个索引列表,该列表应该被删除。然后,我循环索引并从初始列表中删除项目。这看起来像这样:

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

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);
    }
}
耳钉梦 2024-08-14 14:31:32

用属性追踪要移除的元素,处理后将其全部移除。

using System.Linq;

List<MyProperty> _Group = new List<MyProperty>();
// ... add elements

bool cond = false;
foreach (MyProperty currObj in _Group)
{
    // here it is supposed that you decide the "remove conditions"...
    cond = true; // set true or false...
    if (cond) 
    {
        // SET - element can be deleted
        currObj.REMOVE_ME = true;
    }
}
// RESET
_Group.RemoveAll(r => r.REMOVE_ME);

Trace the elements to be removed with a property, and remove them all after process.

using System.Linq;

List<MyProperty> _Group = new List<MyProperty>();
// ... add elements

bool cond = false;
foreach (MyProperty currObj in _Group)
{
    // here it is supposed that you decide the "remove conditions"...
    cond = true; // set true or false...
    if (cond) 
    {
        // SET - element can be deleted
        currObj.REMOVE_ME = true;
    }
}
// RESET
_Group.RemoveAll(r => r.REMOVE_ME);
扎心 2024-08-14 14:31:32
myList.RemoveAt(i--);

simples;
myList.RemoveAt(i--);

simples;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文