リスト(List)の列挙中にリスト要素を削除するには?[C#、VB]:.NET TIPS
foreach文でリスト内の要素を列挙中に、いずれかの要素を削除しようとすると例外が発生して失敗する。そんな場合は、for文やRemoveAllメソッドを応用する。C#およびVBでの使い方や注意点を解説する。
リスト(=System.Collections.Generic名前空間のList<T>クラスのオブジェクト)に対して、foreach文により要素を列挙している最中には、そのリストから要素を削除することはできない。これを行おうとするとInvalidOperation例外が発生する。以下はそのコード例である。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
List<string> lines = new List<string>();
lines.Add("Hello");
lines.Add("World");
lines.Add("Hello");
lines.Add("Again");
foreach (string s in lines) {
// ここで要素に対する何かの処理
if (s == "Hello") {
lines.Remove(s); // 要素の削除
}
}
// InvalidOperation例外が発生
foreach (string s in lines) {
Console.WriteLine(s);
}
}
}
Imports System
Imports System.Collections.Generic
Class Program
Shared Sub main()
Dim lines As New List(Of String)()
lines.Add("Hello")
lines.Add("World")
lines.Add("Hello")
lines.Add("Again")
For Each s As String In lines
' ここで要素に対する何かの処理
If s = "Hello" Then
lines.Remove(s) ' 要素の削除
End If
' InvalidOperation例外が発生
Next
For Each s As String In lines
Console.WriteLine(s)
Next
End Sub
End Class
これは、要素の削除により、すべての要素を順に列挙するという動作が保証できなくなるためだ(要素の追加やソートも同様)。この挙動は、ほとんどのコレクション・クラスに共通する挙動である。
forループによる要素の削除
上記のように、リスト内の各要素に対して何かの処理を行いながら、不要となる要素を削除したい場合には、for文とインデックス番号を使ってリストの要素を列挙していけばよい。
ただし、末尾の要素から先頭方向に向かって処理していく必要がある*1。これは次のように記述できる。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
List<string> lines = new List<string>();
lines.Add("Hello");
lines.Add("World");
lines.Add("Hello");
lines.Add("Again");
for (int i = lines.Count - 1; i >= 0; i--) {
// ここで要素に対する何かの処理
if (lines[i] == "Hello") {
lines.Remove(lines[i]); // 要素の削除
}
}
foreach (string s in lines) {
Console.WriteLine(s);
}
// 出力:
// World
// Again
}
}
Imports System
Imports System.Collections.Generic
Class Program
Shared Sub main()
Dim lines As New List(Of String)()
lines.Add("Hello")
lines.Add("World")
lines.Add("Hello")
lines.Add("Again")
For i As Integer = lines.Count - 1 To 0 Step -1
' ここで要素に対する何かの処理
If lines(i) = "Hello" Then
lines.Remove(lines(i)) ' 要素の削除
End If
Next
For Each s As String In lines
Console.WriteLine(s)
Next
' 出力:
' World
' Again
End Sub
End Class
*1 先頭から順に処理してもInvalidOperation例外は発生しないが、正しい結果とならないケースがあり得る。例えばリストの内容が、"Hello"、"Hello"、"World"の場合を考えてみてほしい。
RemoveAllメソッドによる要素の削除
なお、単に特定の条件に一致する要素をリストから削除したいだけであれば、RemoveAllメソッドを使うと便利だ。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
List<string> lines = new List<string>();
lines.Add("Hello");
lines.Add("World");
lines.Add("Hello");
lines.Add("Again");
lines.RemoveAll(checkLine);
foreach (string s in lines) {
Console.WriteLine(s);
}
// 出力:
// World
// Again
}
static bool checkLine(string s) {
return s == "Hello"; // この条件の要素をすべて削除
}
}
Imports System
Imports System.Collections.Generic
Class Program
Shared Sub main()
Dim lines As New List(Of String)()
lines.Add("Hello")
lines.Add("World")
lines.Add("Hello")
lines.Add("Again")
lines.RemoveAll(AddressOf checkLine)
For Each s As String In lines
Console.WriteLine(s)
Next
' 出力:
' World
' Again
End Sub
Shared Function checkLine(ByVal s As String) As Boolean
Return s = "Hello" ' この条件の要素をすべて削除
End Function
End Class
ちなみに、この場合にはラムダ式を使うと、処理をもっと簡潔に記述できる。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
List<string> lines = new List<string>();
lines.Add("Hello");
lines.Add("World");
lines.Add("Hello");
lines.Add("Again");
lines.RemoveAll(s => s == "Hello");
foreach (string s in lines) {
Console.WriteLine(s);
}
// 出力:
// World
// Again
}
}
Imports System
Imports System.Collections.Generic
Class Program
Shared Sub main()
Dim lines As New List(Of String)()
lines.Add("Hello")
lines.Add("World")
lines.Add("Hello")
lines.Add("Again")
lines.RemoveAll(Function(s As String) s = "Hello")
For Each s As String In lines
Console.WriteLine(s)
Next
' 出力:
' World
' Again
End Sub
End Class
ただしラムダ式は、Visual Studio 2008(C# 3.0やVB 9.0)以降でのみ記述可能である。
利用可能バージョン:.NET Framework 2.0以降
カテゴリ:クラス・ライブラリ 処理対象:コレクション
使用ライブラリ:Listクラス(System.Collections.Generic名前空間)
■この記事と関連性の高い別の.NET TIPS
Copyright© Digital Advantage Corp. All Rights Reserved.