Javaの場合
import java.util.ArrayList; import java.util.List; class Hoge { public static void main(String []args) { List<Integer> samples = new ArrayList<Integer>(); samples.add(0); samples.add(1); samples.add(2); samples.add(3); samples.add(4); samples.add(5); samples.add(6); samples.add(7); samples.add(8); samples.add(9); System.out.println(samples); for (Integer i : samples) { if (i % 3 == 0) { samples.remove(i); } } System.out.println(samples); } }
というコードを書いて、実行してみると、
$ javac Hoge.java $ java Hoge [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Exception in thread "main" java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:449) at java.util.AbstractList$Itr.next(AbstractList.java:420) at Hoge.main(Hoge.java:20)
となる。
import java.util.ArrayList; import java.util.List; class Hoge { public static void main(String []args) { List<Integer> samples = new ArrayList<Integer>(); samples.add(0); samples.add(1); samples.add(2); samples.add(3); samples.add(4); samples.add(5); samples.add(6); samples.add(7); samples.add(8); samples.add(9); samples.add(0); System.out.println(samples); java.util.Iterator<Integer> iter = samples.iterator(); while (iter.hasNext()) { if (iter.next() % 3 == 0) { iter.remove(); } } System.out.println(samples); } }
と書くと、
$ javac Hoge.java $ java Hoge [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0] [1, 2, 4, 5, 7, 8]
と、正しく3の倍数を除いたものが得られる。
ConcurrentModificationExceptionというのは、繰り返し処理の途中でCollectionが変更されたときにスローされるらしい。
Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle
1番目のコードはモロにそういうことをしているので、見事にスローされてしまい失敗しているようだ。
2番目のコードは、そのIteratorが行う処理としてremoveをしているので、それがサポートされていればIteratorの動作を保証したまま要素の削除を行うことができる、ということらしい。
Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle
必ずしもremoveがサポートされているわけでもないようで、例えば以下のようなコードを書くと
import java.util.Arrays; import java.util.List; class Hoge { public static void main(String []args) { List<Integer> samples = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); System.out.println(samples); java.util.Iterator<Integer> iter = samples.iterator(); while (iter.hasNext()) { if (iter.next() % 3 == 0) { iter.remove(); } } System.out.println(samples); } }
こういう結果になる。
$ javac Hoge.java $ java Hoge [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Exception in thread "main" java.lang.UnsupportedOperationException at java.util.AbstractList.remove(AbstractList.java:172) at java.util.AbstractList$Itr.remove(AbstractList.java:437) at java.util.AbstractCollection.remove(AbstractCollection.java:255) at Hoge.main(Hoge.java:12)
Pythonの場合
Javaのように例外がスローされることもなく、普通にfor文の繰り返しの中で要素の削除をすることができてしまう。
#!/usr/bin/env python samples = [1, 2, 3, 4, 5, 6, 7, 8, 9] for elem in samples: if elem % 3 == 0: samples.remove(elem) print samples
$ python hoge.py [1, 2, 4, 5, 7, 8]
実行結果も正しいものが得られるようにみえる。
が、以下のように変更してみると、結果が期待に反することが分かる。
#!/usr/bin/env python samples = [0, 1, 2, 3, 3, 4, 5, 6, 6, 6, 7, 8, 9, 9, 9, 9] for elem in samples: if elem % 3 == 0: samples.remove(elem) print samples
$ python hoge.py [1, 2, 3, 4, 5, 6, 7, 8, 9, 9]
3の倍数が幾つか残っている。
おそらく、要素が削除されたときに詰められて、Iteratorが見ている先が変化してしまうから?
| [0, 1, 2, 3, 3, 4, 5, 6, 6, 6, 7, 8, 9, 9, 9, 9] | [0, 1, 2, 3, 3, 4, 5, 6, 6, 6, 7, 8, 9, 9, 9, 9] | [0, 1, 2, 3, 3, 4, 5, 6, 6, 6, 7, 8, 9, 9, 9, 9] | [0, 1, 2, 3, 3, 4, 5, 6, 6, 6, 7, 8, 9, 9, 9, 9] (ここで"3"の要素が削除される) | [0, 1, 2, 3, 4, 5, 6, 6, 6, 7, 8, 9, 9, 9, 9] | [0, 1, 2, 3, 4, 5, 6, 6, 6, 7, 8, 9, 9, 9, 9] ...
ということで、普通に書けるし実行できてしまうけど、このように「繰り返し処理の中で要素を削除する」ということはすべきではないようだ。
pythonにはfilterというビルトイン関数があるので、フィルタリングしたいときはそれを使って新しいリストを生成することができる。
#!/usr/bin/env python samples = [0, 1, 2, 3, 3, 4, 5, 6, 6, 6, 7, 8, 9, 9, 9, 9] print filter(lambda x: False if x % 3 == 0 else True, samples)
$ python hoge.py [1, 2, 4, 5, 7, 8]