После прочтения через zend / zend_hash.h и ext / standard / array.c, я думаю, что нашел ответ (спасибо Крису и Гамбо за предложения).
Массив PHP представляет собой цепочечную хеш-таблицу (поиск O (c) и O (n) при столкновениях клавиш), которая позволяет использовать ключи типа int и string. Он использует 2 разных алгоритма хеширования для размещения двух типов в одном и том же хэш-ключе. Также каждое значение, сохраненное в хэше, связано со значением, сохраненным до него, и значением, сохраненным после (связанный список). Он также имеет временный указатель, который используется для хранения текущего элемента, поэтому хеш может быть повторен.
Подвох для функции array_rand
заключается в том, что для обеспечения того, чтобы ключ был действительно случайным, функция array_rand
должна выполнять итерацию по массиву rand(0, count($array))
раз (O (n)). Это связано с тем, что за время O (c) невозможно перейти к смещению в хэш-таблице, поскольку нет гарантии, что в диапазоне нет пропущенных ключей.
Это открытие несколько обеспокоило меня, потому что это означает, что в PHP НЕТ типа данных, который имеет нормальные характеристики массива C. Сейчас в большинстве случаев это нормально, потому что поиск по хешу очень быстрый, но ошибки обнаруживаются в таких случаях, как array_rand
.
Другая вещь, которая также беспокоила меня, была разница между реализацией array_key_exists
и in_array
. array_key_exists
использует поиск по хешу (большую часть времени O (c)), чтобы увидеть, находится ли ключ в массиве, в то время как in_array
должен линейно искать хеш (O (n)).
Рассмотрим два примера ниже:
in_array версия
$array = range(0, 100000);
if( in_array( $random_key, $array ) ) {
//we found a value
}
array_key_exists version
$array = array_fill_keys( range(0, 100000), NULL );
if( array_key_exists( $random_key, $array ) ) {
//we found a value, err key
}
Хотя версия in_array выглядит намного чище и проще для понимания, она на самом деле очень медленная для больших массивов (O (n)), где array_key_exists (несмотря на досадно длинное имя) очень быстрое (O (c) или закрытое).
В заключение:
Мне бы хотелось, чтобы в структуре данных Zend HashTable был прозрачный флаг, который был бы установлен в случаях, когда массив был создан с использованием array_push
или array[] = $value
, что позволило бы масштабировать как массив C вместо связанного списка.