2016/10/18 20:15:56

PHPの配列の独特な挙動

php-logo
目次(クリックするとジャンプします)
  • 1:配列についてちょっと整理
  • 2:PHPの配列の感じ
  • 3:PHPには表面上「純粋配列」は存在しない
  • 3.1:PHPの配列を観察してみる
  • 3.2:JavaScriptではどうなるか?
  • 4:補足・ハマったポイント
  • 5:まとめ

配列についてちょっと整理

一般に配列には大きく分けて2つ種類があります。

配列の種類 英語 意味
配列 array 非負数の整数の添字によって区別できるデータの集合
連想配列 associative array 添字に任意の文字列(制約は言語によって異なる)をつかう配列

上記の配列は純粋配列などとも呼ばれ、いわゆる数字インデックスで要素を区別できる配列です。

連想配列は連想リスト、連想コンテナ、ディクショナリ(dictionary)、ハッシュ(hash)、マップ(map)など言語によってさまざまな呼び名があるります。

Pythonだとディクショナリ、Rubyだとハッシュ、Javaだとマップが一般的かもしれません。

PHPでは上記でいう配列は添字配列、連想配列はそのまま連想配列と言うことが多いような気がします。(気がするだけです)

PHPの配列の感じ

PHPの配列はこんな感じ。整数添字ですね。

array(6) {
  [0]=>
  int(0)
  [1]=>
  int(1)
  [2]=>
  int(2)
  [3]=>
  int(3)
  [4]=>
  int(4)
  [5]=>
  int(5)
}

連想配列はこんな感じ。

array(4) {
  ["cpu"]=>
  string(7) "celeron"
  ["memory"]=>
  string(3) "4GB"
  ["ssd"]=>
  NULL
  ["hdd"]=>
  string(5) "500GB"

PHPには表面上「純粋配列」は存在しない

重要なことにPHPでは添字配列も連想配列も表面上の違いはなく、連想配列として振る舞います。

上記で言及した純粋配列は表面上存在していません。

つまり整数の添字はあくまでint型のキーというだけで、配列の順番を保証するインデックスではありません。

他の言語に触れるまでこの挙動になんの不思議も感じていませんでしたが、どちらかというとPHPが独特であるようですよ。

PHPの配列を観察してみる

例えばPHPで以下のようなコードを実行してみます。

このコードは配列を出力したあと、unsetで配列の添字1(つまり順番としては2番めの要素)を削除しています。

$theDrifters = ['ikariya', 'shimura', 'takagi', 'nakamoto', 'katou'];
unset($theDrifters[1]);
var_dump($theDrifters);

結果どう出力されるかというと…

array(4) {
  [0]=>
  string(7) "ikariya"
  [2]=>
  string(6) "takagi"
  [3]=>
  string(8) "nakamoto"
  [4]=>
  string(7) "katou"
}

添字1が歯抜けになったのがお分かりいただけるかと思います。0234となっていますよね。

JavaScriptではどうなるか?

では同じようなことをJavaScriptでやってみます。

まず配列を作ってみます。

var theDrifters = ['ikariya', 'shimura', 'takagi', 'nakamoto', 'katou'];
console.log(theDrifters);

これはこう出力されます。

Array[5]
0: "ikariya"
1: "shimura"
2: "takagi"
3: "nakamoto"
4: "katou"

先ほどのPHPのコードと同じように添字1の要素を削除してみます。

var theDrifters = ['ikariya', 'shimura', 'takagi', 'nakamoto', 'katou'];
var shimura = theDrifters.indexOf("shimura");//添字1の要素のインデックスを得る
var ushiro  = theDrifters.splice(ushiro, 1);//添字1の要素を削除(正確には引き抜く) 
console.log(theDrifters);

これはこう出力されます。

Array[4]
0: "ikariya"
1: "takagi"
2: "nakamoto"
3: "katou"

添字は歯抜けにはなっておらず、インデックスが保たれているのがお分かりいただけると思います。

RubyやPythonなどでもこのような挙動になるはずです。

PHPの配列の挙動が独特であることがお分かりいただけるかと思います。

補足・ハマったポイント

PHP7から内部的に純粋配列がサポートされました。0から連続するint型がキーの場合、それは純粋配列として扱われるということです。

通常、処理コストは純粋配列よりキーの解析が必要な連想配列のほうが高いとされます。しかし内部的に純粋配列が実装されたことで処理コストが改善されたということデス。

PHPの純粋配列で注意しないといけないのは「連続したint型のキー」だということで、もし歯抜けにした場合それは連想配列に変換されてしまうようです(はず)

純粋配列は内部的な実装なのでPHPの表面上、配列の見た目と感触は今までと何ら変わりません。

インデックスを歯抜けにするとせっかくの速度アドバンテージが失われてしまう可能性があるので、注意したいと思いマス。

まとめ

  • PHPでは配列(純粋配列)と連想配列の区別は無い
  • PHPでの配列はすべて連想配列のように振る舞う
  • そのためPHPの配列はあくまでint型キーの連想配列と考えるのが妥当
  • しかしPHP7から内部的に純粋配列が実装された
  • 0から連続するint型がキーの場合に内部的に純粋配列として処理される
  • 純粋配列は連想配列より処理が速い