Хэш таблицы
В PHP все массивы это упорядоченные словари (т.е они представляют собой упорядоченный список пар вида ключ-значение), где ассоциирование ключа значению реализовано на основе hashtable.
Сделано это для того, чтобы ключами массива могли выступать не только целочисленные типы, а и сложные типы вроде строк. Сам процесс происходит следующим образом — от ключа массива берётся хэш, который представлен целым числом. Это целое число используется как индекс в массиве.
Хеширование (или хэширование, англ. Hashing ) – это преобразование входного массива данных определённого типа и произвольной длины в выходную битовую строку фиксированной длины. Такие преобразования также называются хеш-функциями или функциями свёртки, а их результаты называют хешем, хеш-кодом, хеш-таблицей или дайджестом сообщения (англ. message digest ).
Проблема возникает, когда хэширующая функция возвращает одинаковые хэши для разных ключей, то есть возникает коллизия (то, что это действительно происходит легко убедится – количество возможных ключей бесконечно велико, в то время как количество хэшей ограничено размером типа integer).
Существуют два способа борьбы с коллизиями. Первый – открытая адресация, когда элемент сохраняется с другим индексом, если текущий уже занят, второй – хранить элементы с одинаковым индексом в связном списке (метод цепочек). PHP использует второй способ.
Хеш-таблица – это структура данных, реализующая интерфейс ассоциативного массива, то есть она позволяет хранить пары вида "ключ - значение" и выполнять три операции: операцию добавления новой пары, операцию поиска и операцию удаления пары по ключу. Хеш-таблица является массивом, формируемым в определённом порядке хеш-функцией.
Самый простой способ хешировать данные на PHP — использовать функцию crc32()
:
// Любые данные которые мы хотим хешировать $checksum = crc32('The quick brown fox jumped over the lazy dog.'); // Для одних и тех же данных хеш всегда одинаковый! print_r($checksum); // => 2191738434
С хешированием мы встречаемся в разработке часто. Например, идентификатор коммита в git 0481e0692e2501192d67d7da506c6e70ba41e913
не что иное, как хеш, полученный в результате хеширования данных коммита.
После того как хеш получен, его можно преобразовать в индекс массива, например, через получение остатка от деления:
// $key — это ключ в ассоциативном массиве, который мы хешируем $index = crc32($key) % 1000; // по модулю print_r($index); // => 434
Рассмотрим процесс добавления нового значения в ассоциативный массив. Программист пишет:
$data = []; $data['key'] = 'value';
Такая простая, на первый взгляд, строчка, запускает целый процесс. Ниже его грубое описание, без деталей и с упрощениями:
// Для простоты показано на PHP, хотя в реальности всё это происходит на более низком уровне. // Создание ассоциативного массива приводит к инициализации // индексированного массива внутри интерпретатора. $internal = []; // Во время присвоения значения `$data['key'] = 'value'` // интерпретатор выполняет несколько действий: // 1. Хеширует ключ. Результатом хеширования становится число. $hash = crc32('key'); // 2. Число, полученное на предыдущем шаге преобразуется в индекс массива. $index = $hash % 1000; // В значение внутреннего индексированного массива, по найденному индексу, // записывается ещё один массив, первым элементом которого становится ключ `'key'`, // а вторым значение `'value'`. $internal[$index] = ['key', 'value'];
Теперь посмотрим на чтение:
$data = []; $data['key'] = 'value'; print_r($data['key']);
Интерпретатор хеширует ключ. Результатом хеширования становится число. Это число используется как индекс внутреннего массива для поиска значения. Если индекс существует, то извлекается значение, которое находилось внутри, и возвращается наружу.