none
LinkedListの要素の削除について RRS feed

  • 質問

  • こんにちは。お世話になります。

     

    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からその要素を削除したいのですが、

    この書き方ですとエラーがでました。

     

    こういう場合はどうやって書いたらよいのでしょうか?

    よろしくお願いします。
    2008年12月3日 7:06

回答

  • そもそも、Remove(T) は検索が入るため適さないでしょう。Remove(LinkedListNode<T>) を使用する必要があります。

    First から順に Next で調べていって、条件に合致したのを Remove していけばいいんじゃないですか?
    2008年12月3日 8:26

すべての返信

  • ちなみに、とくにforeachにはこだわりません。

     

    c++だったらイテレータとか使うところでしょうか?

    c#の場合はまだ慣れていないものでこの辺の使い方が良くわかっていません。

    foreachより効率のいい書き方があればそれで結構ですのでよろしくお願いします。

    2008年12月3日 7:12
  • そもそも、Remove(T) は検索が入るため適さないでしょう。Remove(LinkedListNode<T>) を使用する必要があります。

    First から順に Next で調べていって、条件に合致したのを Remove していけばいいんじゃないですか?
    2008年12月3日 8:26
  • なるほど。だんだん分かってきました。

    ありがとうございました。

    2008年12月3日 9:01
  •  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 Snippet
    for (int i = 0; i < coll.Count; i++)
    {
      coll[i].age += 1;
      if (coll[i].age >= 100)
        coll.RemoveAt(i);
    }

     

    このようなループを使用することで、コレクションのもつ列挙を行わないという手があります。しかし、このコードは間違っていて、後ろからループするとか、抜けた番号をうまく処理するとか、ちゃんとした工夫が必要になってきます。LinkedList に限定すれば、ノードを追いかけることが効率的なので
     
    Code Snippet
    LinkedListNode 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 Snippet
    hoges = new LinkedList(new List(hoges).RemoveAll(delegate(CHoge hoge)
    {
      hoge.age += 1;
     
      return hoge.age >= 100;
    }));

     

     
    みたいな感じになりますね。このコードでは hoges が再構成されてしまうので、それが問題になる場合には多少の工夫が必要になりますし、問題にならない場合でもあまり可読性の高いコードではないですね。
    2008年12月3日 13:02
  •  K.Takaoka さんからの引用

    これに対してどのように解決するかは、いくつかの方法があって、

     
    1) 列挙を自分で行う
    2) マーク&スイープ
    3) 削除可能な列挙方法を用いる
     
    というものがすぐにあげられます。他にも方法はあるでしょう。たとえば、(1) は

    9) LINQを使うとか

    2008年12月4日 16:40
  •  karashima さんからの引用

    9) LINQを使うとか

     

    delete 構文はサポートされていませんよね...?

    2008年12月4日 23:38
  •  K.Takaoka さんからの引用
     karashima さんからの引用

    9) LINQを使うとか

    delete 構文はサポートされていませんよね...?


    # 削除するというのは、

    # 結局、必要なモノだけを残すという事なので

    必要なモノをselectすればどうかな、という事です。
    2008年12月5日 6:13