fastutilの注意点

WEB+DB PRESS Vol.60で、「fastutilとsuxによる大規模データ処理」と題して、プリミティブコレクションライブラリのfastutilと、簡潔データ構造ライブラリのsuxを紹介する記事を書きました。

WEB+DB PRESS Vol.60

WEB+DB PRESS Vol.60

fastutil: http://fastutil.dsi.unimi.it/
sux: http://sux.dsi.unimi.it/

fastutilは特に汎用性が高く、いろいろな場面で使えるライブラリだと思うのですが、記事に挙げていない注意点も若干あるので、以下に書きたいと思います。

メソッド名の取り違えによる思わぬautoboxingの発生

fastutilでは、プリミティブリストはjava.util.Listと、プリミティブマップはjava.util.Mapと互換性を持っていて便利なのですが、その反面、get()メソッドがjava.util.List#get()やjava.util.Map#get()を実装しているため、何気なくget()メソッドを使うとautoboxingが発生してしまいます。

例えばIntArrayListには以下の2種類のgetメソッドがあり、プリミティブ値を直接取得するには後者のgetInt()メソッドを呼ぶ必要があります。

  • Integer get(int index);
  • int getInt(int index);

Mapのイテレーションが遅い

fastutilのパフォーマンスは、全体的にMahout CollectionsやTroveよりも優秀なのですが、Mapのイテレーションに関しては劣っています。

Mahout CollectionsやTroveのMap実装では、以下のように内部イテレータを使って高速なイテレーションができるのですが、fastutilにはそれがありません。

// Mahout Collectionsの場合
OpenIntIntHashMap map = new OpenIntIntHashMap();
...
map.forEachPair(new IntIntProcedure() {
    @Override
    public boolean apply(int key, int value) {
        // 何らかの処理
        return true;
    }
});

fastutilのマップで高速なイテレーションをしたい場合は、各マップクラスを継承して、内部イテレータを実装した方が良いと思います。

そこまでしたくないという場合、fastutilでは以下のようにfastIteratorという外部イテレータが使えます。記述が少し冗長になりますが、通常のイテレータよりは高速です。

Int2IntMap map = new Int2IntOpenHashMap();
...
Int2IntMap.FastEntrySet entrySet = (Int2IntMap.FastEntrySet) map.int2IntEntrySet();
ObjectIterator<Int2IntMap.Entry> it = entrySet.fastIterator();
while (it.hasNext()) {
    Int2IntMap.Entry e = it.next();
    int key = e.getIntKey();
    int value = e.getIntValue();
    // 何らかの処理
}