libGDXとはWindows、Linux、Mac、Android、iPhone、HTMLに対応したクロスプラットフォームゲームライブラリです。有名なIngressでも利用されています。欧米ではわりとメジャーどころです。
#Map使ってますか?
前回はListの代わりにArrayを使おうというものでした。今回はMapの代わりにObjectMapを使おうという安易なネタです。
こちらも軽量かつ、パフォーマンスが出るように調整されています。
ソースの行数はObjectMapが771行。HashMapは2380行です。HashMapも例によってAbstractMapを継承しておりそちらも含めると規模の大きさが違うのがわかりますね。
#ObjectMapもコレクションフレームワークではない
こちらもArray同様コレクションインターフェースを実装していません。実装しているのはイテレータのみ。
そのため単純な置き換えはできません。パフォーマンスを求められるところで利用しましょう。
個人的にですが、Mapはループの内側で利用することも多いかと思います。
ここが早いかどうかはかなり影響する場面は多いはずです。作業用と割り切って使いましょう。
#使い方
Mapをこんな感じで使ったとします。
Map<Integer,String> map = new HashMap<>();
map.put(1, "1つめ");
map.put(2, "2つめ");
map.put(3, "3つめ");
for(Map.Entry<Integer,String> e : map.entrySet()){
System.out.println(e.getKey() + "=" + e.getValue());
}
ObjectMapはこのようになります。HashMapなので出力される順序は期待しないでください。
ObjectMap<Integer,String> omap = new ObjectMap<>();
omap.put(1, "1つめ");
omap.put(2, "2つめ");
omap.put(3, "3つめ");
for(ObjectMap.Entry<Integer,String> e: omap){
System.out.println(e.key + "=" + e.value);
}
ほとんど同じですね。ObjectMapはそれ自体がIterableを実装しているため、そのままループで描けるのがポイントです。
ほかにもEntryが軽量化しています。インターフェースと実装が分かれているMap.Entryと違い、ObjectMap.EntryはPublicフィールドのみのシンプルなクラスです。ソースは以下のようになっています。
static public class Entry<K, V> {
public K key;
public V value;
public String toString () {
return key + "=" + value;
}
}
#より安全に
Map#get()の引数は型パラメータが使われません。これは歴史的な制限です。かならずObject型になってしまい、安全ではありませんでした。
ObjectMapではMapとの互換性を気にしていないのでちゃんと型パラメータが使われます。Integer以外は利用することができません。
また、Mapはデフォルトメソッドでデフォルト値が使えるようになっていますが、これはJava8からです。Java7や6などAndroidやHTML出力などを利用する場合はJava8ではないため利用することができません。
一方ObjectMapはデフォルトの値を使うgetメソッドが用意されています。
System.out.println(omap.get(1, "でふぉるとばりゅー1"));
System.out.println(omap.get(4, "でふぉるとばりゅー4"));
キーが存在すればその値を、なければ2つ目の引数が返されます。
最初の方で描いたサンプルに上のコードを組み合わせると1つ目のほうは通常のgetメソッドと同じ結果、「1つめ」が返ってきますが、2つ目は「デフォルトバリュー4」という値が表示されます。
ObjectMapはArrayと比べると便利な命令は少ないですが、単純に高速化がわかりやすいので恩恵も大きいと思います。
#ObjectMapの亜種
ArrayにおけるIntArrayなどと同様にMapにもプリミティブ対応版があります。
キーがint固定のもの。型パラメータがバリューのみになっているのがわかります。
IntMap<String> intmap = new IntMap<>();
intmap.put(1, "1つめ");
intmap.put(2, "2つめ");
intmap.put(3, "3つめ");
for(IntMap.Entry<String> e: intmap){
System.out.println(e.key + "=" + e.value);
}
ほかにもlongに対応するLongMap、equalsではなく==で判定するIdentityMapなどもあります。
中でも利用頻度が高いのがIntIntMapです。キーのみならずバリューもプリミティブのint型です。業務アプリでもよく使うタイプのものです。型パラメータはすでにありません。
面白い機能としてバリューからキーを取得する機能が付いています。もちろん、バリューが重なる場合はダメですが、これは使う場面もあるかもしれません。
IntIntMap iimap = new IntIntMap();
iimap.put(1, 100);
iimap.put(2, 200);
iimap.put(3, 300);
System.out.println(iimap.findKey(200, 22));//200を探す
System.out.println(iimap.findKey(400, 44));//400は見つからないのでデフォルトが返される
そのほかに
public int getAndIncrement (int key, int defaultValue, int increment)
といったものもあります。引数名で動きは想像できるかと思います。件数を数えるときなど便利ですね
バリューにfloat型を利用したIntFloatMapがありますが、こちらは利用頻度は多くないと思われます。
これらはObjectMapより高速なので、できる限りこちらを使うとよいでしょう。