トップ回答者
LinkedListの要素の削除について

質問
-
こんにちは。お世話になります。
LinkedList<CHoge> hoges;
foreach(CHoge hoge in hoges)
{
hoge.age += 1;
//ageが100を超えたら削除
if(hoge.age >= 100)
hoges.Remove(hoge);
}
上記のようにLinkedListでもっているCHogeのインスタンスのageメンバを
foreachでプラスしていって、100を超えているものがあったら
LinkedListからその要素を削除したいのですが、
この書き方ですとエラーがでました。
こういう場合はどうやって書いたらよいのでしょうか?
よろしくお願いします。
回答
すべての返信
-
oreore さんからの引用 LinkedListからその要素を削除したいのですが、
この書き方ですとエラーがでました。
発生した例外について調べてみましたか?LinkedList に限らず、ほぼすべてのコレクションは列挙作業の途中で構成要素に変更があると、例外が発生するようになっています。 LinkedList を例にすれば、LinkedList は中身を一列にCode Snippet○-[A]-[B]-[C]-[D]-[E]-○のように保持しているコレクションです。○は先端と終端です。ここで列挙を始めると、A, B, C, D, E という値が順番に手に入ります。今回、C の age が 99 になっていて削除されると仮定しましょう。hoge = C となっている状態で、Remove(C) を実行すると、リストはCode Snippet○-[A]-[B]-[D]-[E]-○○-[C]-○になります。この状態で、次へ進もうとする・・・つまり、C の次を取り出そうとするとおかしなことになるのはわかりますね? このような変更を感知したため、例外が発生したのです。# Code Snippet の外に書いたら emoticon になって面白かったこれに対してどのように解決するかは、いくつかの方法があって、1) 列挙を自分で行う2) マーク&スイープ3) 削除可能な列挙方法を用いるというものがすぐにあげられます。他にも方法はあるでしょう。たとえば、(1) はCode Snippetfor (int i = 0; i < coll.Count; i++){coll[i].age += 1;if (coll[i].age >= 100)coll.RemoveAt(i);}このようなループを使用することで、コレクションのもつ列挙を行わないという手があります。しかし、このコードは間違っていて、後ろからループするとか、抜けた番号をうまく処理するとか、ちゃんとした工夫が必要になってきます。LinkedList に限定すれば、ノードを追いかけることが効率的なのでCode SnippetLinkedListNode node = hoges.First;while (node != null){CHoge hoge = node.Value;hoge.age += 1;if (hoge.age >= 100){LinkedListNode removingNode = node;node = node.Next;hoges.Remove(removingNode);}else{node = node.Next}}のような書き方もできるでしょうね。(2) では列挙と削除を分離するという方法です。Code Snippet// 列挙中は、削除の対象を覚えるのみLinkedList removingValues = new LinkedList();foreach (CHoge hoge in hoges){hoge.age += 1;if (hoge.age >= 100)removingValues.Add(hoge);}// 覚えておいた削除する要素を削除するforeach (CHoge hoge in removingValues)hoges.Remove(hoge);といったかんじですね。 (3) に関しては LinkedList には標準的に操作として提供されていませんが、List などであれば提供されているので、Code Snippethoges = new LinkedList(new List(hoges).RemoveAll(delegate(CHoge hoge){hoge.age += 1;return hoge.age >= 100;}));みたいな感じになりますね。このコードでは hoges が再構成されてしまうので、それが問題になる場合には多少の工夫が必要になりますし、問題にならない場合でもあまり可読性の高いコードではないですね。