isset と array_key_exists

昨日の続き。

2. array_key_existsよりハッシュを使え
array_searchは、毎回全データを検索するので遅いです。
データに配列の順序が関係ないなら、連想配列 + issetを使うほうが高速です。
連想配列の場合ハッシュの仕組みが使われるのでその分高速です。
同様に、array_key_existsよりissetのほうがはるかに高速です。

array_search が遅いのはそのとおりだが、array_key_exists だってハッシュを使った検索じゃないかな。
実際、ベンチマーク(コードと結果は後述)を取ると確かに isset のほうが3倍ぐらい速いのであるが、高々3倍であることを考えると array_key_exists だってコストは定数時間な訳で配列の要素数などは関係ないように思える。
そもそも、isset と array_key_exists は配列が null を含む場合の挙動が異なる。以下マニュアルから。

isset() は NULL 値を持つ配列キーに対して TRUE を返しません。一方、array_key_exists() は TRUE を返します。

<?php
$search_array = array('first' => null, 'second' => 4);

// false を返す
isset($search_array['first']);

// true を返す
array_key_exists('first', $search_array);
?>

多くの場面では isset で問題ないであろうが、これらの違いを把握しておかないと思わぬところではまる可能性がある。
ところで、前のエントリは Trackback を送ったはずなんだが、なぜか反映されない。SPAM 判定食らってる?

% cat bench_isset_and_array_key_eixsts.php
<?php
require_once 'Benchmark/Timer.php';

function test_isset($array, $iterate)
{
    for ($i = 0; $i < $iterate; $i++) {
        isset($array[10]);
    }
}

function test_array_key_exists($array, $iterate)
{
    for ($i = 0; $i < $iterate; $i++) {
        array_key_exists(10, $array);
    }
}

$array = range(1, 10000);

$timer = new Benchmark_Timer();
$timer->start();
test_isset($array, 100000);
$timer->setMarker('isset');
test_array_key_exists($array, 100000);
$timer->setMarker('array_key_exists');
$timer->stop();
$timer->display();
?>

% php bench_isset_and_array_key_eixsts.php
---------------------------------------------------------------
marker             time index            ex time         perct
---------------------------------------------------------------
Start              1161976988.52912700   -                0.00%
---------------------------------------------------------------
isset              1161976988.55793900   0.028812        27.05%
---------------------------------------------------------------
array_key_exists   1161976988.63562300   0.077684        72.93%
---------------------------------------------------------------
Stop               1161976988.63564000   0.000017         0.02%
---------------------------------------------------------------
total              -                     0.106513       100.00%
---------------------------------------------------------------