JavaとC#のListアクセス時の動作の違い

C#でListの要素を削除するコードを書いて実行したところInvalidOperationExceptionが発生した。
最初は原因がわからなかったが、foreachの途中でListの要素を削除したことによってループを継続することができなくなったようだ。

C#

[csharp]
try
{
List<String> list = new List<string>();
list.Add("OK");
list.Add("OK");
list.Add("NG");
list.Add("OK");

foreach (String str in list) {
if (str == "NG") {
list.Remove(str);
}
}
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
Console.ReadLine();
}
[/csharp]

出力結果

場所 System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
場所 System.Collections.Generic.List`1.Enumerator.MoveNextRare()
場所 System.Collections.Generic.List`1.Enumerator.MoveNext()
場所 TestCSharp.Program.Main(String[] args)

IEnumeratorインターフェースのmoveNextを利用してループしても同様の結果となった。
ループの途中でListの要素を削除するには以下のようにindex指定でアクセスする必要がある。

[csharp]
List<String> list = new List<string>();
list.Add("OK");
list.Add("OK");
list.Add("NG");
list.Add("OK");

for (int i = 0; i < list.Count; i++ )
{
if (list[i] == "NG") {
list.RemoveAt(i);
}
}

foreach (String str in list) {
Console.WriteLine(str);

}
[/csharp]

OK
OK
OK

ちなみにJavaならIteratorでも問題なく削除できる。
[java]
List<String> list = new ArrayList<String>();
list.add("OK");
list.add("OK");
list.add("NG");
list.add("OK");

Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
if (itr.next().equals("NG")) {
itr.remove();
}
}

System.out.println(list.toString());
[/java]

出力結果

[OK, OK, OK]

言語が変わると思わぬ動作の違いに驚いたりしますね。