HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

イテレーションの最中に要素を削除する

Javaで次のようなプログラム:

Map<String, String> map = new HashMap<String, String>();
map.put("a", "A");
map.put("b", "B");
map.put("c", "C");
for (String key : map.keySet()) {
    if (key.equals("b")) {
        map.remove(key);
    }
}

を書くと、java.util.ConcurrentModificationExceptionが発生する(ことがある)*1。どうしてもイテレーションの最中に要素を削除したい場合は、次のように書く:

Map<String, String> map = new HashMap<String, String>();
map.put("a", "A");
map.put("b", "B");
map.put("c", "C");
System.out.println(map);
for (Iterator<String> it = map.keySet().iterator(); it.hasNext();) {
    String key = it.next();
    if (key.equals("b")) {
        it.remove();
    }
}


さて、ここからが本題。
PHPで次のようなコードを書いたらどうなるか。

<?php
    function test1() {
        $map = array("a" => "A", "b" => "B", "c" => "C");
        foreach ($map as $key => $value) {
            if ($key === "b") {
                $map[$key] = null;
            }
        }
        print_r($map);
    }
    function test2() {
        $map = array("a" => "A", "b" => "B", "c" => "C");
        foreach ($map as $key => $value) {
            if ($key === "b") {
                unset($map[$key]);
            }
        }
        print_r($map);
    }
    function test3() {
        $map = array("a" => "A", "b" => "B", "c" => "C");
        foreach ($map as $key => &$value) {
            if ($key === "b") {
                $value = null;
            }
        }
        print_r($map);
    }
    function test4() {
        $map = array("a" => "A", "b" => "B", "c" => "C");
        foreach ($map as $key => &$value) {
            if ($key === "b") {
                unset($value);
            }
        }
        print_r($map);
    }
    test1();
    test2();
    test3();
    test4();
?>

結果は次のような感じ。

Array
(
    [a] => A
    [b] =>
    [c] => C
)
Array
(
    [a] => A
    [c] => C
)
Array
(
    [a] => A
    [b] =>
    [c] => C
)
Array
(
    [a] => A
    [b] => B
    [c] => C
)

期待通りに削除されているのは、

unset($map[$key]);

の場合のみ。Javaに慣れているとちょっと抵抗があるコードだが、PHPの場合は逆にこう書かないとだめらしい。

*1:jdk1.6.0_22では、"b"、"c"のいずれを削除してもjava.util.ConcurrentModificationExceptionが発生するが、jdk1.5.0_22では、"b"を削除した場合には同例外が発生しない。