SplDoublyLinkedListとは
PHPには標準ライブラリ(SPL)として、様々なデータ構造が用意されています。その中の一つ SplDoublyLinkedList は、**双方向リスト(Doubly Linked List)**を実装したクラスです。
双方向リストとは、各要素が「前の要素」と「次の要素」への参照を持つデータ構造です。通常の配列と異なり、先頭・末尾への追加・削除が**O(1)(定数時間)**で行える点が特徴です。
既存記事でも紹介している SplStack や SplQueue は、実はこの SplDoublyLinkedList を継承して作られています。今回はその土台となる SplDoublyLinkedList 自体の使い方を理解しましょう。
基本的な使い方
要素の追加
要素の追加は先頭・末尾どちらにも行えます。
<?php
$list = new SplDoublyLinkedList();
// 末尾に追加
$list->push('apple');
$list->push('banana');
$list->push('cherry');
// 先頭に追加
$list->unshift('mango');
echo $list->count(); // 4
| メソッド | 説明 |
|---|---|
push($value) | 末尾に要素を追加 |
unshift($value) | 先頭に要素を追加 |
要素の取得・削除
<?php
$list = new SplDoublyLinkedList();
$list->push('first');
$list->push('second');
$list->push('third');
// 先頭の要素を取得(削除しない)
echo $list->bottom(); // first
// 末尾の要素を取得(削除しない)
echo $list->top(); // third
// 末尾から取り出して削除
echo $list->pop(); // third
// 先頭から取り出して削除
echo $list->shift(); // first
echo $list->count(); // 1
インデックスを使ったアクセス
SplDoublyLinkedList は配列のようにインデックスでもアクセスできます。
<?php
$list = new SplDoublyLinkedList();
$list->push('a');
$list->push('b');
$list->push('c');
// インデックスで取得
echo $list->offsetGet(0); // a
echo $list->offsetGet(2); // c
// インデックスで上書き
$list->offsetSet(1, 'B');
echo $list->offsetGet(1); // B
// インデックスで削除
$list->offsetUnset(0);
echo $list->bottom(); // B
イテレーション(繰り返し処理)
SplDoublyLinkedList は Iterator インターフェースを実装しているため、foreach で繰り返し処理できます。
また、イテレーションモードを切り替えることで、前から辿るか後ろから辿るかを制御できます。
<?php
$list = new SplDoublyLinkedList();
$list->push('one');
$list->push('two');
$list->push('three');
// デフォルト(先頭から末尾)
$list->setIteratorMode(SplDoublyLinkedList::IT_MODE_FIFO);
foreach ($list as $value) {
echo $value . PHP_EOL;
}
// one
// two
// three
// 逆順(末尾から先頭)
$list->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO);
foreach ($list as $value) {
echo $value . PHP_EOL;
}
// three
// two
// one
イテレーションモードの組み合わせ
モードは | で組み合わせられます。
// 末尾から辿り、辿った要素を削除する
$list->setIteratorMode(
SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_DELETE
);
foreach ($list as $value) {
echo $value . PHP_EOL;
}
echo $list->count(); // 0(全削除された)
| モード定数 | 説明 |
|---|---|
IT_MODE_FIFO | 先頭から末尾へ(デフォルト) |
IT_MODE_LIFO | 末尾から先頭へ |
IT_MODE_KEEP | 要素を残す(デフォルト) |
IT_MODE_DELETE | 辿った要素を削除する |
実践的な活用例:履歴管理
双方向リストは「直近の操作履歴を管理する」ユースケースに向いています。以下はシンプルなコマンド履歴クラスの例です。
<?php
class CommandHistory
{
private SplDoublyLinkedList $history;
private int $maxSize;
public function __construct(int $maxSize = 5)
{
$this->history = new SplDoublyLinkedList();
$this->maxSize = $maxSize;
}
public function record(string $command): void
{
// 上限を超えたら先頭(最古)を削除
if ($this->history->count() >= $this->maxSize) {
$this->history->shift();
}
$this->history->push($command);
}
public function showLatest(): void
{
// 末尾(最新)から表示
$this->history->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO);
foreach ($this->history as $command) {
echo $command . PHP_EOL;
}
}
}
$history = new CommandHistory(maxSize: 3);
$history->record('git init');
$history->record('git add .');
$history->record('git commit -m "first"');
$history->record('git push'); // 上限超過 → "git init"が削除される
echo "=== 最新の履歴 ===" . PHP_EOL;
$history->showLatest();
// git push
// git commit -m "first"
// git add .
配列との使い分け
| 観点 | 配列 | SplDoublyLinkedList |
|---|---|---|
| 先頭への追加 | O(n)(遅い) | O(1)(速い) |
| 末尾への追加 | O(1) | O(1) |
| インデックスアクセス | O(1) | O(n) |
| メモリ効率 | 大量要素で有利 | 参照オーバーヘッドあり |
先頭や末尾への頻繁な追加・削除が必要な場合は SplDoublyLinkedList が有利です。一方、インデックスによるランダムアクセスが多い場合は素直に配列を使いましょう。
まとめ
SplDoublyLinkedListは双方向リストを実装したSPLクラスpush/pop/unshift/shiftで先頭・末尾の操作が可能- イテレーションモードで繰り返しの方向や削除挙動を制御できる
- 先頭・末尾への追加・削除が多いシナリオで配列より効率的
SplStack や SplQueue を使う前にその親クラスである SplDoublyLinkedList を理解しておくと、SPL全体への理解が深まります。ぜひ活用してみてください!