开发者

the fastest way to pick the Nth element of a hash

I've got a big hashtable (array with string indexes) and lo开发者_如何学运维oking for a function that quickly picks the first (ideally, also Nth) element from it. array_shift() and reset() are too slow for my needs.

UPDATE: i'm also not looking for a reference-based solution, the function should accept expressions as in get_first(some_func_returning_array())

ANSWER array_slice method (kudos Gumbo) seems to be the winner. Complete benchmarking code

function bigary($n) {
    $a = array();
    $s = range('A', 'Z');
    do {
        shuffle($s);
        $a[substr(implode('', $s), rand(10, 20))] = $n;
    } while(--$n);
    return $a;
}

function timeit($name, $fn) {
    global $results;

    $loops = 1000;
    $size  = 5432;

    static $a;
    if(!$a) $a = bigary($size);

    $t = microtime(1);
    for($i = 0; $i < $loops; $i++)
        $b = $fn($a);
    $results[$name] = microtime(1) - $t;
}

timeit('dummy', function ($a) { 
    // benchmark php function call overhead
});

timeit('array_shift', function ($a) { 
    return array_shift($a); 
});

timeit('reset', function ($a) { 
    return reset($a); 
});

timeit('foreach', function ($a) { 
    foreach($a as $b) return $b;
});

timeit('keys', function ($a) { 
    $b = array_keys($a); 
    return $a[$b[0]];
});

timeit('values', function ($a) { 
    $b = array_values($a); 
    return $b[0];
});

timeit('slice', function ($a) { 
    $b = array_slice($a, 0, 1); 
    return reset($b);
});

asort($results);

foreach($results as $name => $time)
    printf("%20s = %.3f\n", $name, $time);

Results:

           dummy = 0.393
           slice = 0.433
          values = 0.824
         foreach = 0.929
           reset = 0.935
     array_shift = 0.954
            keys = 1.371


Use array_slice to get an array of just the n-th item and array_pop to finally get it:

$nthItem = array_pop(array_slice($arr, $n, 1));


Your benchmark may be flawed, since:

$fn = function ($a) { 
    return array_shift($a); 
};
timeit('array_shift', $fn);

array_shift = 1.242 (5432)

and

timeit('array_shift', array_shift);

array_shift = 0.026 (4433)

but also

$fn = function ($a) { } 
timeit('empty lambda', $fn);

empty lambda = 0.501 (0)

That being said, another possible solution:

$v = array_values($a);
return $v[ $index ];

Sample Code:

$t = microtime(1);
$v = array_values($a); // cached
while($loops--) {
    $b = $v[$loops];
}

array_values = 0.002 (5432)


I don't think you can get any faster than reset()/end() for first/last element.

As for N'th element, I can't think of anything better than this:

function (&$hashmap, $n) {
    $keys = array_keys($hashmap);
    return $hashmap[$keys[$n]];
}

Of course for performance this can tuned to have a pre-cached $keys array (if hashmap doesn't change very often). In that case maybe even 0'th element retrieval may be faster than with reset()


To get the first:

function array_first(array $array) {
     foreach ($array as $value) return $value;
     return false;
}

And although I haven't tested this for speed, you could try using array_values() to convert the array to a numerically indexed array.


To get the first element try to reset the array's pointer with reset ( array &$array ). Then get the current value with current(). To get the Nth element use the end(), to set the pointer to the last item. At this point you should be able to get the Nth item with current().

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜