PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

Hash table w PHP – in_array vs isset, SplQueue, SplFixedArray

by Henryk Tews / wtorek, 08 października 2019 / Opublikowano w Algorytmy

Tablica w PHP to jeden z najczęściej używanych typów danych, ale mało kto zastanawia się jak działa pod spodem. PHP array to w rzeczywistości hash table – struktura danych, która pozwala na dostęp do elementów w czasie O(1). Pokazuję jak to działa, jakie ma konsekwencje praktyczne i gdzie tablice PHP zaskakują swoją wydajnością.

Czym jest hash table?

Hash table to struktura danych przechowująca pary klucz-wartość. Gdy zapisujesz element pod kluczem, funkcja haszująca zamienia klucz na indeks w wewnętrznej tablicy. Gdy chcesz odczytać wartość – ta sama funkcja wyznacza indeks w O(1).

<?php

// PHP array jako hash table - dostęp O(1) niezależnie od rozmiaru
$users = [
    'jan.kowalski' => ['id' => 1, 'role' => 'admin'],
    'anna.nowak'   => ['id' => 2, 'role' => 'user'],
    'piotr.wisz'   => ['id' => 3, 'role' => 'user'],
];

// O(1) - bez względu na to czy masz 3 czy 300 000 wpisów
$user = $users['jan.kowalski'];

in_array() vs isset() – O(n) vs O(1)

To jeden z najczęstszych błędów wydajnościowych w PHP. in_array() przeszukuje tablicę liniowo – sprawdza każdy element po kolei. isset() na tablicy asocjacyjnej korzysta z hash table – jest O(1):

<?php

$allowedSkus = ['SKU-001', 'SKU-002', 'SKU-003', /* ... 10 000 SKU */];

// O(n) - w najgorszym przypadku sprawdza wszystkie elementy
if (in_array('SKU-999', $allowedSkus)) {
    // ...
}

// Przepisz na tablicę asocjacyjną - O(1) zawsze
$allowedSkusMap = array_flip($allowedSkus);
// ['SKU-001' => 0, 'SKU-002' => 1, ...]

if (isset($allowedSkusMap['SKU-999'])) {
    // O(1) - natychmiastowy dostęp przez hash
}

Przy 10 elementach różnica jest niezauważalna. Przy 10 000 elementach sprawdzanych w pętli po każdym requeście – już bardzo realna. W Magento 2 taki wzorzec pojawia się np. przy walidacji SKU, sprawdzaniu uprawnień czy filtrowaniu kolekcji produktów w pamięci.

PHP array jako kolejka i stos

PHP array implementuje też interfejsy kolejki (FIFO) i stosu (LIFO) przez wbudowane funkcje:

<?php

// Stos (LIFO) - push/pop
$stack = [];
array_push($stack, 'pierwszy');
array_push($stack, 'drugi');
array_push($stack, 'trzeci');

echo array_pop($stack); // 'trzeci' - ostatni wchodzi, pierwszy wychodzi
echo array_pop($stack); // 'drugi'

// Krótszy zapis dla stosu
$stack[] = 'element'; // push
$last = array_pop($stack); // pop

// Kolejka (FIFO) - enqueue/dequeue
$queue = [];
array_push($queue, 'pierwsze zadanie');
array_push($queue, 'drugie zadanie');

echo array_shift($queue); // 'pierwsze zadanie' - FIFO
echo array_shift($queue); // 'drugie zadanie'

Uwaga: array_shift() jest O(n) – po usunięciu pierwszego elementu PHP przenumerowuje wszystkie klucze numeryczne. Jeśli potrzebujesz wydajnej kolejki na tysiącach elementów, rozważ SplQueue z biblioteki SPL.

SplStack i SplQueue – gdy wydajność ma znaczenie

<?php

// SplStack - O(1) dla push i pop
$stack = new \SplStack();
$stack->push('pierwszy');
$stack->push('drugi');
echo $stack->pop(); // 'drugi'

// SplQueue - O(1) dla enqueue i dequeue
$queue = new \SplQueue();
$queue->enqueue('zadanie 1');
$queue->enqueue('zadanie 2');
echo $queue->dequeue(); // 'zadanie 1'

// SplMinHeap - kolejka priorytetowa, O(log n) dla insert i extract
$heap = new \SplMinHeap();
$heap->insert(['priority' => 3, 'task' => 'Niski priorytet']);
$heap->insert(['priority' => 1, 'task' => 'Wysoki priorytet']);
$heap->insert(['priority' => 2, 'task' => 'Średni priorytet']);

while (!$heap->isEmpty()) {
    $item = $heap->extract();
    echo $item['task'] . PHP_EOL;
}
// Wysoki priorytet
// Średni priorytet
// Niski priorytet

Zużycie pamięci – PHP array vs SplFixedArray

PHP array jest wygodna, ale pamięciożerna – każdy element przechowuje dodatkowe metadane (typ klucza, typ wartości, wskaźniki do poprzedniego i następnego elementu w ordered map). Przy dużych zbiorach numerycznych SplFixedArray zużywa znacznie mniej pamięci:

<?php

$size = 100000;

// Zwykła tablica PHP
$start = memory_get_usage();
$arr = range(0, $size - 1);
$phpArrayMemory = memory_get_usage() - $start;

// SplFixedArray - tylko indeksy numeryczne, stały rozmiar
$start = memory_get_usage();
$fixed = SplFixedArray::fromArray(range(0, $size - 1));
$fixedMemory = memory_get_usage() - $start;

echo 'PHP array:     ' . number_format($phpArrayMemory / 1024 / 1024, 2) . ' MB' . PHP_EOL;
echo 'SplFixedArray: ' . number_format($fixedMemory / 1024 / 1024, 2) . ' MB' . PHP_EOL;
// PHP array:     ~8 MB
// SplFixedArray: ~2 MB

Podsumowanie

PHP array to jeden z najbardziej wszechstronnych typów danych w jakimkolwiek języku programowania – hash table, stos, kolejka i lista w jednym. Kluczowe rzeczy do zapamiętania: dostęp przez klucz to O(1), in_array() to O(n) – zastępuj przez isset() na odwróconej tablicy gdy wydajność ma znaczenie, a przy dużych zbiorach numerycznych rozważ SplFixedArray.

About Henryk Tews

Co możesz przeczytać następne

LRU Cache – O(1) implementacja z HashMap + lista dwukierunkowa, per-request cache
Dijkstra – najkrótsza ścieżka z wagami, SplMinHeap, zastosowania w e-commerce
Sortowanie w PHP – bubble sort, merge sort, usort() i operator <=>
  • Publikacje
  • O autorze
  • Kontakt

© 2026 Created by

GÓRA
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Zawsze aktywne
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Zarządzaj opcjami
  • Zarządzaj serwisami
  • Zarządzaj {vendor_count} dostawcami
  • Przeczytaj więcej o tych celach
Zobacz preferencje
  • {title}
  • {title}
  • {title}