535

それぞれが処理する必要のある要素のリストを操作し、結果に応じてリストから削除するためのより良いパターンを探しています。

(例外が発生するため).Remove(element)内で使用することはできません...また、を使用することはできません。また、コレクション内の現在の位置を混乱させるためです。foreach (var element in X)Collection was modified; enumeration operation may not execute.for (int i = 0; i < elements.Count(); i++).RemoveAt(i)i

これを行うためのエレガントな方法はありますか?

4

28 に答える 28

870

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));
于 2009-10-17T14:31:26.987 に答える
126
 foreach (var item in list.ToList()) {
     list.Remove(item);
 }

「.ToList()」をリスト (または LINQ クエリの結果)に追加すると、恐ろしい「 Collection was modified; enumeration operation may not execute」なしで、「list」から「item」を直接削除できます。エラー。配列を安全に削除できるように、コンパイラは「リスト」のコピーを作成します。

このパターンは非常に効率的ではありませんが、自然な感触があり、ほとんどすべての状況に柔軟に対応できます。各「アイテム」をDBに保存し、DBの保存が成功した場合にのみリストから削除したい場合など。

于 2015-01-08T23:34:56.410 に答える
90

シンプルで簡単な解決策:

コレクションに対して逆方向に実行する標準の for ループを使用しRemoveAt(i)て、要素を削除します。

于 2009-10-17T14:25:05.650 に答える
76

Collection を反復しながら Collection から要素を削除したい場合、最初に頭に浮かぶのは逆反復です。

幸いなことに、不必要な入力を伴い、エラーが発生しやすい 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);
    }
}
于 2012-05-10T19:39:21.583 に答える
25

ジェネリック リストで ToArray() を使用すると、ジェネリック リストで Remove(item) を実行できます。

        List<String> strings = new List<string>() { "a", "b", "c", "d" };
        foreach (string s in strings.ToArray())
        {
            if (s == "b")
                strings.Remove(s);
        }
于 2009-10-17T14:46:09.250 に答える
24

不要な要素を削除しようとするのではなく、必要な要素を選択してください。これは、要素を削除するよりもはるかに簡単です (そして一般的に効率的です)。

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

以下のMichael Dillonが残したコメントに応じて、これをコメントとして投稿したかったのですが、長すぎて、とにかく私の回答に含めるとおそらく役に立ちます。

個人的には、アイテムを1つずつ削除することは決してありません.削除が必要な場合RemoveAllは、述語を取り、内部配列を1回だけ再配置する呼び出しを呼び出しますが、削除するすべての要素に対して操作を行いますRemove. はるかに効率的です。Array.CopyRemoveAll

また、リストを逆方向に反復する場合、削除する要素のインデックスが既にあるため、 を呼び出す方がはるかに効率的RemoveAtです。Remove最初にリストを走査して要素のインデックスを見つけるためです。削除しようとしていますが、そのインデックスは既に知っています。

Remove全体として、for ループを呼び出す理由はありません。そして理想的には、可能であれば、上記のコードを使用して必要に応じてリストから要素をストリーミングし、2 番目のデータ構造をまったく作成する必要がないようにします。

于 2009-10-17T14:48:59.727 に答える
23

.ToList() を使用すると、この質問で説明されているように、リストのコピーが作成されます: ToList()-- 新しいリストを作成しますか?

ToList() を使用すると、実際にはコピーを繰り返し処理しているため、元のリストから削除できます。

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

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

     }
于 2013-08-08T10:52:43.387 に答える
17

削除するアイテムを決定する関数に副作用がなく、アイテムを変更しない場合 (純粋な関数)、シンプルで効率的な (線形時間) ソリューションは次のとおりです。

list.RemoveAll(condition);

副作用がある場合は、次のようなものを使用します。

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

ハッシュが適切であると仮定すると、これは依然として線形時間です。ただし、ハッシュセットが原因でメモリ使用量が増加します。

最後に、リストがIList<T>a ではなく aのみの場合は、この特別な foreach イテレータを実行するにはどうすればよいですか?List<T>への回答をお勧めします。. これには、他の多くの回答の二次ランタイムと比較して、の典型的な実装が与えられた線形ランタイムがあります。IList<T>

于 2014-12-22T11:32:26.650 に答える
13

削除は使用できる条件で行われるため

list.RemoveAll(item => item.Value == someValue);
于 2014-12-22T10:58:23.570 に答える
10
List<T> TheList = new List<T>();

TheList.FindAll(element => element.Satisfies(Condition)).ForEach(element => TheList.Remove(element));
于 2012-10-24T08:23:03.820 に答える
9

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 ループ中にアイテムを削除できる独自のコレクション クラスと反復子を作成することは十分に可能です。)

于 2015-02-18T21:37:36.457 に答える
6

リストを繰り返し処理しているときにRemoveorを使用することは、意図的に困難にされています。巧妙なトリックで動作させることができるかもしれませんが、非常に遅くなります。呼び出すたびに、リスト全体をスキャンして、削除する要素を見つける必要があります。呼び出すたびに、後続の要素を 1 位置左に移動する必要があります。そのため、またはを使用する解は、二次時間O(n²)を必要とします。RemoveAtRemoveRemoveAtRemoveRemoveAt

可能であれば使用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);
于 2016-10-09T21:12:43.907 に答える
4

保持したくない要素を除外する LINQ クエリからリストを再割り当てします。

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

リストが非常に大きい場合を除き、これを行っても重大なパフォーマンスの問題は発生しません。

于 2009-10-17T14:23:21.090 に答える
4

反復処理中にリストからアイテムを削除する最良の方法は、 を使用すること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;
});
于 2017-10-29T15:32:29.307 に答える
3

predicateが要素のブール型プロパティであると仮定すると、それが true の場合、要素を削除する必要があります。

        int i = 0;
        while (i < list.Count())
        {
            if (list[i].predicate == true)
            {
                list.RemoveAt(i);
                continue;
            }
            i++;
        }
于 2014-07-30T09:28:05.913 に答える
0

私は、与えられ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
}
于 2013-09-19T05:24:54.230 に答える
0

リストからアイテムを削除するコストは、削除するアイテムに続くアイテムの数に比例します。アイテムの前半が削除の対象となる場合、アイテムを個別に削除することに基づくアプローチでは、最終的に約 N*N/4 個のアイテム コピー操作を実行する必要があり、リストが大きい場合は非常にコストがかかる可能性があります。 .

より迅速なアプローチは、リストをスキャンして最初に削除するアイテム (存在する場合) を見つけ、その時点から、保持する必要がある各アイテムをそれが属する場所にコピーすることです。これが完了すると、R 項目を保持する必要がある場合、リストの最初の R 項目がそれらの R 項目になり、削除が必要なすべての項目が最後になります。これらのアイテムが逆の順序で削除された場合、システムはそれらのいずれもコピーする必要がなくなるため、リストに最初の F のすべてを含む R アイテムが保持されている N アイテムがある場合は、次のようにする必要があります。 RF アイテムをコピーし、リストを NR 回 1 アイテムずつ縮小します。すべての線形時間。

于 2016-04-18T17:29:01.133 に答える
-4
myList.RemoveAt(i--);

simples;
于 2015-11-06T14:10:19.470 に答える