コレクションフレームワークを拡張するCollections現場に活かすJakarta Project(7)

» 2003年06月06日 00時00分 公開
[横田健彦(株)東芝 研究開発センター]

 前回「DBのコネクションプーリングを簡単に実現」に引き続き、Commonsサブプロジェクトの中から利用価値の高いコンポーネントを紹介しましょう。今回と次回は、Commonsの中でもユーティリティ色の強いコンポーネントを扱います。今回扱うCollectionsは、Javaのコレクションフレームワークを拡張するクラス群です。

Javaコレクションフレームワークを拡張する「Collections」

 上述したように、CollectionsはJavaのコレクションフレームワーク(コレクションフレームワークについてはhttp://java.sun.com/j2se/1.4/ja/docs/ja/guide/collections/index.htmlを参照してください)を拡張するクラス群です。この原稿の執筆時点での最新バージョンは2.1です。

 Collectionsには50以上のクラスが含まれているため、ここですべてのクラスについて詳細に説明することはできません。そこで利用頻度が高いものや知っておくと便利なものをいくつかピックアップして説明することにします。なお、Collectionsが持つクラスの詳細について知りたい方はJakartaのCollectionsのWebページ(http://jakarta.apache.org/commons/collections.html)を参照してください。

 ここではCollectionsが持つクラスのうちorg.apache.commons.collectionsパッケージに属するものについて説明することにします(表1参照)。

表1 org.apache.commons.collectionsパッケージに属するクラス
クラス 説明
FastArrayList
FastHashMap
FastTreeMap
既存のArrayList、HashMap、TreeMapの高速版
CollectionUtils java.util.Collectionに関するユーティリティメソッドを集めたクラス
ComparatorUtils さまざまなComparatorを提供するユーティリティクラス
IteratorUtils Iteratorに関するユーティリティメソッドを集めたクラス

より高速に動作する「FastArrayList」「FastHashMap」「FastTreeMap」

 FastArrayList、FastHashMap、FastTreeMapはそれぞれjava.utilパッケージのArrayList、HashMap、TreeMapの高速版です。ただし常にこれらのクラスより高速に動作するわけではなく、マルチスレッドで用いる場合の読み出し(get)処理が高速に動作します。従って、マルチスレッドで動作するプログラムにおいて、書き込み処理よりも読み出し処理が多く行われるようなコレクションを必要とする場面で使用することで、プログラムのパフォーマンスを向上させることができます。例えば、プログラムの開始時点で設定ファイルを読み込んでコレクションに設定を一度書き込んでしまえば後はコレクションから設定を読み出すだけ、といった場合に用いると有効です。

 Fast系のコレクションクラスは低速モードと高速モードのいずれかのモードを取ります。通常は低速モードの状態で初期値をセットし、以降は高速モードに切り替えて主に値の読み出しを行います。サンプルプログラムをリスト1に示します。このプログラムはFastArrayListを生成して初期値を設定したうえで返すようなメソッドです。まず(1)でFastArrayListのインスタンスを生成します。生成直後のモードは低速モードです。次に(2)で値を設定します。値の設定が終わったら(3)のようにFastArrayList#setFast()メソッドで高速モードに切り替えます。このように、インスタンスを生成して値を設定した後には忘れずに高速モードに切り替えることによって以降の読み出しパフォーマンスを向上させることができます。

リスト1 FastArrayListの使用例
   public FastArrayList initializeList(Object[] objs)
{
    FastArrayList falist = new FastArrayList();  (1)
    for (int i = 0; i < objs.length; i++) {
        falist.add(objs[i]);                     (2)
    }
    falist.setFast(true);                        (3)

    return falist;
}

ユーティリティメソッドを集めた「CollectionUtils」

 CollectionUtilsはCollectionオブジェクトに関するstaticなユーティリティメソッドを多数持つユーティリティクラスです。CollectionUtilsが持っているメソッドのうち代表的なものを表2に示します。

表2 CollectionUtilsクラスの代表的なメソッド
メソッド名 説明
addAll 指定されたEnumeration/Iteratorから得られるすべてのオブジェクトを指定されたCollectionに追加
reverseArray 指定された配列を逆順に並べ替える
predicatedCollection 指定された条件を満たすオブジェクトだけを保持するCollectionを生成して返す
filter 指定されたCollectionについて、指定された条件を満たすオブジェクトを残し、そうでないものを取り除くフィルタ処理を行う
find 指定されたCollectionから指定された条件を満たす最初のオブジェクトを発見する
select 指定された条件を満たすすべてのオブジェクトを返す
forAllDo 指定されたCollectionが持つそれぞれのオブジェクトについて、指定された処理を実行する
transform 指定されたCollectionが持つそれぞれのオブジェクトを、指定された変換ルールに従って変換する

 また、CollectionUtilsにはCollectionを集合と見なして集合演算を行うためのメソッドが含まれています。集合演算を行うためのメソッドを表3に示します。

表3 集合演算を行うためのメソッド
メソッド名 説明
union(a, b) aとbの和集合を返す
disjunction(a, b) aとbの排他的な和集合を返す
subtract(a, b) aとbの差集合を返す
intersection(a, b) aとbの積集合を返す
isEqualCollection(a, b) aとbが集合として同一かどうかを判定する
isSubCollection(a, b) aがbの部分集合であるかどうかを判定する
isProperSubCollection(a, b) aがbの完全部分集合であるかどうかを判定する
containsAny(a, b) aとbに共通要素があるかどうかを判定する

 addAllメソッドとfilterメソッドの使用例をリスト2に示します。リスト2は数値の配列から負でない数値だけを取り出すプログラムです。

リスト2 CollectionUtilsの使用例
   import java.util.*;
import org.apache.commons.collections.*;

public class Sample2
{
    public static void main(String[] args)
        throws Exception
    {
        Number[] nums = new Number[]{ new Integer(5),
            new Integer(-1), new Integer(1), new Integer(9),
            new Integer(-5) };

        List list = new ArrayList(nums.length);
        CollectionUtils.addAll(list, nums);         (1)

        for (int i = 0; i < list.size(); i++) {
            System.out.println("element[" + i + "] = "
                + list.get(i));
        }

        CollectionUtils.filter(list, new Predicate() { (2)
            public boolean evaluate(Object input)
            {
                return (((Number)input).intValue() >= 0); (3)
            }
        });

        for (int i = 0; i < list.size(); i++) {
            System.out.println("filtered element[" + i + "] = "
                + list.get(i));
        }
    }
}

 まず(1)でaddAllメソッドを用いて空のListオブジェクトに配列numsの内容を挿入します。その後に(2)でListの中から負でない数値だけを残すようなフィルタリング処理を行います。(2)のfilterメソッドには第1引数として処理対象とするListオブジェクトを、第2引数としてフィルタリングのためのルールを指定します。フィルタリングのルールとしてはorg.apache.commons.collections.Predicateインターフェイスの実装クラスを与えます。Predicateインターフェイスはbooleanを返すevaluateというメソッドを持っており、このメソッドがtrueを返すようなオブジェクトだけを残すようにフィルタリング処理が行われます。今回のケースでは(3)のようにオブジェクトが負でない数値のときにtrueを返すようにします。

 このプログラムを実行すると、以下のような結果が出力されます。

element[0] = 5
element[1] = -1
element[2] = 1
element[3] = 9
element[4] = -5
filtered element[0] = 5
filtered element[1] = 1
filtered element[2] = 9

さまざまなComparatorを提供「ComparatorUtils」

 ComparatorUtilsはさまざまなComparatorを提供するユーティリティクラスです。ComparatorUtilsが持っているメソッドのうち代表的なものを表4に示します。

表4 ComparatorUtilsクラスの代表的なメソッド
メソッド名 説明
min 2つのオブジェクトのうち、指定されたComparatorを使って比較した際に小さい方を返す
max 2つのオブジェクトのうち、指定されたComparatorを使って比較した際に大きい方を返す
naturalComparator Comparableインターフェイスの実装クラスのオブジェクトを自然な順序で並べるようなComparatorを返す
chainedComparator 複数のComparatorを連鎖したComparatorを返す
nullHighComparator 与えられたComparatorを基に、null値を非null値より大きいと見なすようなComparatorを生成して返す
nullLowComparator 与えられたComparatorを基に、null値を非null値より小さいと見なすようなComparatorを生成して返す
reversedComparator 指定されたComparatorと逆順にオブジェクトを並べるようなComparatorを返す

 naturalComparatorとreversedComparatorの使用例をリスト3に示します。リスト3は数値のリストを降順に並べ替えるプログラムです。

リスト3 ComparatorUtilsの使用例
import java.util.*;
import org.apache.commons.collections.*;

public class Sample3
{
    public static void main(String[] args)
        throws Exception
    {
        Number[] nums = new Number[]{ new Integer(5),
            new Integer(3), new Integer(1), new Integer(2),
            new Integer(4) };

        List list = new ArrayList(nums.length);
        CollectionUtils.addAll(list, nums);       (1)

        for (int i = 0; i < list.size(); i++) {
            System.out.println("element[" + i + "] = "
                + list.get(i));
        }

        Collections.sort(list,                    (2)
            ComparatorUtils.reversedComparator(
            ComparatorUtils.naturalComparator()));

        for (int i = 0; i < list.size(); i++) {
            System.out.println("sorted element[" + i + "] = "
                + list.get(i));
        }
    }
}

 まず(1)で並べ替える元のListオブジェクトを生成します。次に(2)でjava.util.Collectionsクラスのsortメソッドを用いてListオブジェクトを並べ替えます。ここでsortメソッドの第2引数として並べ替えのためのComparatorを与えていますが、まずnaturalComparatorメソッドを使って自然な順序づけのComparatorを取得し、それをreversedComparatorメソッドの引数に与えることで自然な順序づけと逆順のComparatorを取得してsortメソッドに与えています。これでListオブジェクトが降順に並べ替えられます。

 このプログラムを実行すると、以下のような結果が出力されます。

element[0] = 5
element[1] = 3
element[2] = 1
element[3] = 2
element[4] = 4
sorted element[0] = 5
sorted element[1] = 4
sorted element[2] = 3
sorted element[3] = 2
sorted element[4] = 1

Iteratorに関するユーティリティメソッドを集めた「IteratorUtils」

 IteratorUtilsはIteratorに関するユーティリティメソッドやIteratorのデコレータを提供するユーティリティクラスです。IteratorUtilsが持っているメソッドのうち代表的なものを表5に示します。

表5 ComparatorUtilsクラスの代表的なメソッド
メソッド名 説明
arrayIterator 配列のためのIteratorを返す
asEnumeration IteratorをEnumerationに変換する
asIterator EnumerationをIteratorに変換する
toArray Iteratorから得られるオブジェクトの配列を返す
toList Iteratorから得られるオブジェクトのListを返す
chainedIterator 複数のIteratorをつなげたIteratorを返す
collatedIterator 複数のIteratorから得られるオブジェクトを、指定されたComparatorで表される順番で取得するためのIteratorを返す
filteredIterator 指定されたIteratorから得られるオブジェクトのうち、指定された条件を満たすものだけを取得するためのIteratorを返す

 arrayIteratorとcollatedIteratorの使用例をリスト4に示します。リスト4は昇順に並んでいる2つの数値の配列を昇順を保ったままマージするプログラムです。

リスト4 IteratorUtilsの使用例
import org.apache.commons.collections.*;

public class Sample4
{
    public static void main(String[] args)
        throws Exception
    {
        Number[] nums1 = new Number[]{ new Integer(2),         (1)
            new Integer(3), new Integer(6), new Integer(7),
            new Integer(9) };
        Number[] nums2 = new Number[]{ new Integer(1),
            new Integer(4), new Integer(5), new Integer(8),
            new Integer(10) };

        Iterator itr1 = IteratorUtils.arrayIterator(nums1);    (2)
        Iterator itr2 = IteratorUtils.arrayIterator(nums2);

        Iterator itr = IteratorUtils.collatedIterator(         (3)
            ComparatorUtils.naturalComparator(),
            itr1, itr2);

        while (itr.hasNext()) {
            Object obj = itr.next();
            System.out.println(obj);
        }
    }
}

 IteratorUtils.collatedIteratorメソッドは複数のIteratorをマージしたIteratorを返しますが、マージしたIteratorのnextメソッドは元のIteratorのnextメソッドが返すオブジェクトのうち一番「小さい」ものから順に返していきます(何が「小さい」のかという判断はcollatedIteratorメソッドに与えるComparatorで行います)。例えば { 1, 5, 7 } と { 3, 4, 6 } という2つの数列をマージしたIteratorはまず1と3を比較して1を返し、次に5と3を比較して3を返し、……というようにして結局 { 1, 3, 4, 5, 6, 7 } を返します。このように、collatedIteratorを用いると昇順の2つの数列を昇順を保ったままでマージすることができるというわけです。

 リスト4に話を戻しましょう。リスト4ではまず(1)でマージする2つの配列を用意します。次に(2)でarrayIteratorメソッドを用いて配列のIteratorを生成しておきます。生成した2つのIteratorを(3)でcollatedIteratorメソッドを用いて1つのIteratorにマージします。昇順を保ったままマージするために、Comparatorとして自然な順序づけを表すComparatorを与えます。あとは取得したIteratorから順番に値を取り出せば2つの配列のマージ結果を得ることができます。

 今回はCollectionsコンポーネントをご紹介しました。今回ご紹介したクラスやメソッド以外にもCollectionsは有用なクラスや便利なメソッドを数多く持っていますので、興味を持った方はこの機会にぜひ一度Jakarta ProjectのWebサイトを訪れてご自分のプログラミングに利用できるパーツはないかCollectionsについて調べてみてください。

 次回はjava.langパッケージに属するクラスに関するユーティリティを集めたLangコンポーネントについて説明します。

筆者プロフィール

横田健彦(よこた たけひこ)

東京工業大学卒業後、(株)東芝に入社。現在、知識メディアラボラトリーにてコミュニティベース情報共有システムの研究に従事。小学校のころからコンピュータに触れ、主にゲームプログラミングを通してBASIC、アセンブラをはじめとする多数の言語を学ぶ。JavaではJakartaプロジェクトの成果物を利用していく中で主にWebアプリケーションプログラミングの面白さに引かれ、Ja-Jakartaプロジェクトの活動に貢献する一方でオープンソースのJavaベースのWebコンテンツ管理システムであるKvasir/Soraの開発を行っている。



Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。