忍者ブログ

VB.NET-TIPS などプログラミングについて

VB.NETのTIPS(小技集)を中心に、Javascript、PHP その他のプログラミングについて少し役に立つ情報を発信します。いわゆる個人的な忘備録ですが、みなさんのお役に立てれば幸いです。

コレクション「List」の列挙中における要素の削除について

コレクションクラスの ListFor Each 等で要素の削除を行いたい場合があります。 List の要素の削除ぐらい簡単だと思っていたのですが、上手くいかなかった方法と、解決方法について記します。


■簡単に考えていた List からの要素の削除(Remove

ListFor Each で要素を順次取得し、その要素値で削除してみました。
以下のソースを見て下さい。

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ' Listの生成(初期化)
        Dim list As New List(Of Integer) From {0, 1, 10, 100, 1, 1000, 1, 10000}

        ' 列挙中に削除する
        Console.WriteLine("=== Remove ===")
        For Each i As Integer In list
            Console.WriteLine("要素値「{0}」の表示", i)
            If i = 1 Then
                ' 要素値「1」が削除対象
                Console.WriteLine("要素値「{0}」の削除", i)
                list.Remove(i)
            End If
        Next

    End Sub

実行結果がコンソールには以下様に表示されます。
2番目の要素「1」は削除されたのですが、その直後 Next のところで System.InvalidOperationException のエラーが発生してしまいます。 For Each で要素を順次取得中に List の内容を変更したため内部的に位置がずれ、エラーが発生した様です。

=== Remove ===
要素値「0」の表示
要素値「1」の表示
要素値「1」の削除


■解決方法その1・List を配列の様に扱い最後尾から削除(RemoveAt

上記の方法では先頭から削除処理を行ったので内部的にずれが発生しエラーとなったので、 今度は最後尾から削除すれば行けるのではないかと以下のソースの様に変更しました。

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ' Listの生成(初期化)
        Dim list As New List(Of Integer) From {0, 1, 10, 100, 1, 1000, 1, 10000}

        ' 列挙中に削除する
        Console.WriteLine("=== Remove ===")
        Dim nVal As Integer
        Dim i As Integer
        For i = list.Count - 1 To 0 Step -1
            nVal = list.Item(i)
            Console.Write("{0}番目に取得したListの要素:{1}", i, nVal)
            If nVal = 1 Then
                '要素値「1」の削除(Index指定)
                list.RemoveAt(i)
                Console.Write("...削除", i)
            End If
            Console.WriteLine("")
        Next

        Console.WriteLine("=== 削除後 ===")
        i = 0
        For Each nVal In list
            Console.WriteLine("{0}番目に取得したListの要素:{1}", i, nVal)
            i += 1
        Next
    End Sub

結果は以下の通りです。最後尾から順次、値が「1」のものが削除され、最後に一覧してみると値が「1」の要素は確かに削除されています。

=== Remove ===
7番目に取得したListの要素:10000
6番目に取得したListの要素:1...削除
5番目に取得したListの要素:1000
4番目に取得したListの要素:1...削除
3番目に取得したListの要素:100
2番目に取得したListの要素:10
1番目に取得したListの要素:1...削除
0番目に取得したListの要素:0
=== 削除後 ===
0番目に取得したListの要素:0
1番目に取得したListの要素:10
2番目に取得したListの要素:100
3番目に取得したListの要素:1000
4番目に取得したListの要素:10000


■解決方法その2・ToArray メソッドと For Each による削除(Remove

今度は For Each を使うのですが、最初の失敗した方法では無く ListToArray メソッドにより配列として取得する方法を使います。 ToArray メソッドは List を別の配列として生成し、その配列から順次その要素を取得することになります。
直接 List にアクセスするのではなく、一旦別の配列にしてそれにアクセスするので当初の様にエラーが発生しません。

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ' Listの生成(初期化)
        Dim list As New List(Of Integer) From {0, 1, 10, 100, 1, 1000, 1, 10000}

        ' 列挙中に削除する
        Console.WriteLine("=== Remove ===")
        Dim nVal As Integer
        Dim i As Integer
        For Each nVal In list.ToArray
            Console.Write("{0}番目に取得したListの要素:{1}", i, nVal)
            If nVal = 1 Then
                '要素値「1」の削除
                list.Remove(nVal)
                Console.Write("...削除", i)
            End If
            Console.WriteLine("")
        Next

        Console.WriteLine("=== 削除後 ===")
        i = 0
        For Each nVal In list
            Console.WriteLine("{0}番目に取得したListの要素:{1}", i, nVal)
            i += 1
        Next
    End Sub

結果は以下の通りです。先頭から順次、値が「1」のものを削除します。 最後に一覧してみると値が「1」の要素は確かに削除されています。

=== Remove ===
0番目に取得したListの要素:0
0番目に取得したListの要素:1...削除
0番目に取得したListの要素:10
0番目に取得したListの要素:100
0番目に取得したListの要素:1...削除
0番目に取得したListの要素:1000
0番目に取得したListの要素:1...削除
0番目に取得したListの要素:10000
=== 削除後 ===
0番目に取得したListの要素:0
1番目に取得したListの要素:10
2番目に取得したListの要素:100
3番目に取得したListの要素:1000
4番目に取得したListの要素:10000

関連する記事

コレクション「List」の使い方について
コレクション「List」と配列の相互変換について
コレクション「Dictionary」の使い方について
コレクション「Dictionary」から配列及び List への変換について
配列の使い方について(Dim, Redim)
配列の使い方の注意点について(コピー, Clone)
配列の範囲指定によるコピー(Array.Copy, Skip, Take)











PR

コメント

コメントを書く