Массив в PHP один тип на все случаи: список, словарь, стек, очередь, множество. На собесе спрашивают, как он устроен, как приводятся ключи и где ловушки со ссылками.
Устройство
Массив это упорядоченная хеш-таблица (HashTable): элементы лежат в порядке вставки, а хеш-индекс позволяет найти элемент по ключу в среднем за O(1). Если ключи идут подряд с нуля, используется компактный packed-режим без хеш-части.
Следствия:
foreachвсегда идёт в порядке добавления;isset($a[$k])иarray_key_existsработают за O(1), аin_arrayза O(n);- множество делают ключами:
$seen[$x] = true.
Ключи
Ключом бывает только int или string. Остальное приводится:
$a = [1 => 'a', "1" => 'b', true => 'c', 1.7 => 'd'];
count($a); // 1: всё попало в ключ 1
$a["05"] = 'x'; // строка "05" остаётся строкой
Автоключ $a[] = ... берёт максимальный целый ключ плюс один.
Копирование и ссылки
Массивы имеют семантику значения, но копируются лениво (copy-on-write): $b = $a и передача в функцию бесплатны, пока никто не меняет массив. Объекты внутри массива не копируются: копируются дескрипторы.
Ловушка foreach по ссылке:
$arr = [1, 2, 3];
foreach ($arr as &$v) {}
foreach ($arr as $v) {} // перезаписывает последний элемент
print_r($arr); // [1, 2, 2]
После цикла по ссылке всегда unset($v).
Объединение
array_merge([1, 2], [3, 4, 5]); // [1, 2, 3, 4, 5]: числовые ключи перенумерованы
[1, 2] + [3, 4, 5]; // [1, 2, 5]: только отсутствующие ключи
[...$a, ...$b]; // как array_merge (строковые ключи с PHP 8.1)
Для строковых ключей array_merge берёт последнее значение, а + первое.
Функции на каждый день
array_map,array_filter(сохраняет ключи!),array_reduce,array_column,array_combine.array_keys,array_values,array_flip,array_key_first,array_key_last.array_unique,array_diff,array_intersectи их_key-варианты (быстрее, по хешу).array_slice,array_splice,array_chunk.array_is_list(8.1): ключи 0..n-1 по порядку.
После array_filter или unset массив может стать «дырявым», и json_encode выдаст объект вместо списка. Лечится array_values.
Сортировки
sort (значения, ключи сбрасываются), asort (значения, ключи сохраняются), ksort (по ключам), usort, uasort, uksort (свой компаратор), r-варианты по убыванию. С PHP 8.0 все сортировки стабильны. Компаратор удобно писать через <=>:
usort($users, fn($a, $b) => [$b->score, $a->name] <=> [$a->score, $b->name]);
Производительность
array_shiftна большом списке O(n): перенумерация ключей. Для очередейSplQueueили указатель.in_arrayв цикле даёт O(n²): заменяйте наissetпо ключам.- Миллион элементов занимает десятки мегабайт: большие данные обрабатывайте генераторами.
Частые ошибки
foreachпо ссылке безunset.array_mergeв цикле (каждый раз копия): собирайте массив массивов и делайте одинarray_merge(...$parts).- Числовые строки-ключи, которые неожиданно становятся
int. in_arrayбез строгого режима.
Что спросят на собесе
- Как устроен массив PHP и какова сложность операций?
- Чем
array_mergeотличается от+? - Что будет после
foreachпо ссылке? - Как приводятся ключи массива?
- Когда массив реально копируется?