Util

Map (連想配列)

Author : trashtoy

Table of Contents

Map インタフェースの概要

Peach_Util_Map は, PHP の配列機能をオブジェクト指向化した, 連想配列 (キー・バリュー形式) のインタフェースです. Java の コレクションフレームワークの一部 (java.util.Map 等) の API を取り入れつつ, 独自の付加機能を付け加えています. ある程度 Java プログラミングに慣れているユーザーはすんなり使いこなせるでしょう.

既に用意されている実装クラスとして Peach_Util_ArrayMapPeach_Util_HashMap があります. 既存の PHP の配列と同様のデータを扱う場合は Peach_Util_ArrayMap, キーにオブジェクトを利用したい場合は Peach_Util_HashMap を使用してください.

Java の Map インタフェースと以下の相違点があります.

  • キーが存在しない場合に返すデフォルト値の指定が出来る.
  • (Peach_Util_HashMap について) put または get を行う際にキーの等価条件を独自に定義できる (Peach_Util_Equator を参照)
  • capacity 値の動的な変更はサポートせず, 負荷係数などの概念が存在しない. (今後のバージョンでサポートする可能性はある)

SPL をインストールしていない PHP5.2 以前の環境に対応するため, これらのクラスは Iterator などのインタフェースを実装していません.

代替値を指定して値を取得

「もしも $arr["NAME"] があれば, $userName にその値を代入する. なければ代替値として $userName に "anonymous" を代入する」という場合を考えてみます. 通常なら

  1. if (isset($arr["NAME"])) {
  2.     $userName $arr["NAME"];
  3. else {
  4.     $userName "anonymous";
  5. }

となりますが, Map を利用する場合は

  1. $userName $map->get("NAME""anonymous");

のように, 第二引数に代替値を指定して get メソッドを実行することで, 同等のことができます.

既存の配列を Map に変換する

既存のソースコードで配列を使っている箇所を Map に対応させることは簡単にできます.

  1. $arr array("key1" => "AAAA""key2" => "BBBB""key3" => "CCCC");
  2. $map new Peach_Util_ArrayMap($arr)// 指定された配列をそのままマッピングする
  3. $map->get("key2")// => "BBBB"
  4. $map->get("key4")// => NULL

既存の配列を元にして新しい Map を作成する場合, Map に対する操作は元の配列に影響しないので, 安全に使うことができます.

$_GET や $_POST をそのまま使う代わりに Peach_Util_ArrayMap に変換してから利用すれば, ずっと扱いやすくなるでしょう.

もしも, Map オブジェクトを配列に変換したい場合は Peach_Util_ArrayMap::asArray() などを使います. HashMap の場合は Peach_Util_HashMap::entryList() を使ってください.

HashMap と Equator の利用

Peach_Util_HashMap は、オブジェクトをキーとして利用することが出来ます. 以下に利用例を挙げます.

  1. class TestKey
  2. {
  3.     private $id;
  4.     private $name;
  5.  
  6.     public function __construct($id$name)
  7.     {
  8.         $this->id   $id;
  9.         $this->name $name;
  10.     }
  11.  
  12.     public function getId()
  13.     {
  14.         return $this->id;
  15.     }
  16.  
  17.     public function getName()
  18.     {
  19.         return $this->name;
  20.     }
  21. }
  22.  
  23. $k1  new TestKey(1"foo");
  24. $k2  new TestKey(2"bar");
  25. $map new Peach_Util_HashMap();
  26. $map->put($k1100);
  27. $map->put($k2200);
  28.  
  29. echo $map->get(new TestKey(1"foo")"undef")// 100
  30. echo $map->get(new TestKey(2"bar")"undef")// 200
  31. echo $map->get(new TestKey(2"xxx")"undef")// "undef"

上のような使い方でも問題はありませんが, パフォーマンスと安全性のために, HashMap を利用する際には必ずコンストラクタの引数に Equator オブジェクトを設定することを強く推奨します.

以下に, 独自に作成した Equator の例を示します.

  1. class TestKeyEquator implements Peach_Util_Equator
  2. {
  3.     /**
  4.      * 2つの TestKey オブジェクトの id, name がそれぞれ等しい場合に TRUE を返します.
  5.      */
  6.     public function equate($var1$var2)
  7.     {
  8.         return ($var1->getId(=== $var2->getId(&& $var1->getName(=== $var2->getName());
  9.     }
  10.     
  11.     public function hashCode($var)
  12.     {
  13.         return intval($var->getId());
  14.     }
  15. }

独自の Equator を定義するもう一つの利点として, 「必要に応じて等価条件を切り替えることが出来る」という点が挙げられます. 例えば、上に挙げた TestKeyEquator は id と name の両方を比較していましたが, 「name の値に関わらず, id が等しければ同じキーとみなす」ような HashMap が新たに欲しくなった場合は, 以下のように Equator を新しく定義すれば実現することができます.

  1. class TestKeyEquator2 implements Peach_Util_Equator
  2. {
  3.     /**
  4.      * 2つの TestKey オブジェクトの id がそれぞれ等しい場合に TRUE を返します.
  5.      */
  6.     public function equate($var1$var2)
  7.     {
  8.         return ($var1->getId(=== $var2->getId());
  9.     }
  10.     
  11.     public function hashCode($var)
  12.     {
  13.         return intval($var->getId());
  14.     }
  15. }

異なる Equator を持つ複数の HashMap を利用したコードの例を以下に挙げます.

  1. $e1 new TestKeyEquator();
  2. $e2 new TestKeyEquator2();
  3.  
  4. $k1 new TestKey(1"John Smith");
  5. $k2 new TestKey(2"Emily Smith");
  6. $k3 new TestKey(2"Emily Johns");
  7.  
  8. $map1 new Peach_Util_HashMap(null$e1);
  9. $map2 new Peach_Util_HashMap(null$e2);
  10.  
  11. $map1->put($k1"foo");
  12. $map1->put($k2"bar");
  13. $map1->put($k3"baz");
  14. $map2->put($k1"foo");
  15. $map2->put($k2"bar");
  16. $map2->put($k3"baz")// $k2 と $k3 が同一視されるので, ここで $k2 <=> "bar" のマッピングが "baz" に上書きされます
  17.  
  18. echo $map1->size()// => 3
  19. echo $map2->size()// => 2
  20.  
  21. echo $map1->get($k2)// => "bar"
  22. echo $map2->get($k2)// => "baz"