初出 JAVA PRESS Vol.26

Java API ダイジェスト

java.util.Collections

Collectionsクラスはコレクションを活用するためのユーティリティクラスです。すべてのメソッドがstaticメソッドで構成されており、容易に使うことが可能です。

例えば、同期化したコレクションを生成することや、コレクションのソートなどのメソッドなどが含まれます。ここで紹介する以外にもさまざまなユーティリティメソッドが定義されています。


コレクションに作用するメソッド

Collectionsクラスはコレクションを活用するためのメソッドを定義しています。その中でも主だったものを紹介します。

synchronizedXメソッドは同期化したスレッドセーフなコレクションを作成するメソッドです。Xはインタフェースに対応しています。

■ 同期化

変更が行えないコレクションを生成するためにunmodifiableXメソッドを使用します。

■ 変更不可能化

sortメソッドはリストの要素のソートを行います。

■ リストのソート

binarySearchメソッドはリストから指定された要素を検索します。ただし、リストは事前にソートされている必要があります。

■ リストの検索

コレクションを活用するためのユーティリティメソッド

■ 同期化

public static List synchronizedList(List list)
public static Map synchronizedMap(Map m)

複数のスレッドが同時にコレクションに対して要素の追加や削除などを行うと、コレクションが破壊されてしまう可能性があります。これを防ぐためにはコレクションを同期化する必要があります。しかし同期化にはコストがかかるため、ArrayListなどのクラスは同期化を行っていません。

synchronizedList/synchronizedMapメソッドは引数で指定されたリスト/マップを基にした同期化したリスト/マップを返します。これらのメソッドを使用することで複数のスレッドで使用しても安全なコレクションを作成することができます。

ただし、同期化したリストから得られるIteratorオブジェクトは同期化を行っていません。したがって、複数スレッドでIteratorオブジェクトを使用する場合にはリスト1のようにユーザが同期化をする必要があります。

同期化したマップからkeySetなどで得られるSetオブジェクトは同期化しています。しかし、そのSetオブジェクトから取り出したIteratorオブジェクトはリストと同様に同期化していないので、リストの場合と同様にユーザが同期化を行う必要があります。

リスト 1
    List syncList = Collections.synchronizedList(list);
    synchronized (syncList) {
        Iterator it = syncList.iterator();
        while (it.hasNext()) {
            Object obj = it.next();
            // 何らかの処理
        }
    }

■ 変更不可能化

public static List unmodifiableList(List list)
public static Map unmodifiableMap(Map m)

引数のリスト/マップを変更ができないリスト/マップに変更します。このメソッドによって得られたリスト/マップはgetメソッドなどは使用できますが、addメソッドなどのコレクションを変更するようなメソッドをコールするとUnsupportedOperationExceptionがスローされます。リスト2でNGになっている操作がこれに相当します。

また、リスト2に示すように変更不可能なリストから得られるIteratorオブジェクトも変更不可能です。同様にマップのkeySetメソッドなどで得られるSetオブジェクトも変更不可能です。

リスト 2 変更不可能なリストの処理例
    List unmodList = Collections.unmodifiableList(list);
 
    Object obj = unmodList.get(i);       // OK
    int size = unmodList.size(i);        // OK
    unmodList.add(value);                // NG
    unmodList.remove(obj);               // NG
 
    Iterator it = unmodList.iterator();  // OK
    Object obj = it.next();              // OK
    it.remove();                         // NG

■ リストのソート

public static void sort(List list)
public static void sort(List list, Comparator comparator)

リストの要素を昇順にマージソートアルゴリズムを使用してソートします。

前者の引数が1つの sort メソッドは、リストの全ての要素がjava.lang.Comparableインタフェースを実装していなくてはいけません。ComparableインタフェースにはcompareToメソッドが定義されており、このメソッドで値の大小を決定します。

もう一方の sort メソッドはjava.util.Comparatorインタフェースを使用します。Comparatorインタフェースのcompareメソッドでリストの要素同士の大小を比較します。

Collections クラスでリストに対する処理を行う sort や rotate などのメソッドで気をつける点として、引数で指定されたリストが変更されてしまうということがあります。もし、リストが変更されたくない場合はあらかじめリストをコピーしておくなどの処置が必要です。

■ リストの検索

public static int binarySearch(List list, Object key)
public static int binarySearch(List list, Object key, Comparator comparator)

バイナリサーチを使用してリストの要素を検索します。sortメソッドと同様に要素とkeyがComparableインタフェースを実装するか、Comparatorオブジェクトで大小が決められるようにしなくてはなりません。

リスト3にbinarySearchメソッドの使用例を示しました。binarySearchメソッドを使用する場合、事前にリストがソートされている必要があります。リスト3はsortメソッドを使用していますが、sort メソッドの項で説明したように、リストを変更してしまうため変更されたくない場合にはあらかじめコピーを作成し、それに対してソートするようにします。

ListインタフェースにはindexOfメソッドが定義されており、これを利用して要素の検索を行うことができます。しかし、要素数が多くなるとリストの先頭からシーケンシャルに調べていくためパフォーマンスに問題が出てきます。

binarySearchメソッドはバイナリサーチを使用しているので、要素数の増加に対するパフォーマンス劣化が比較的小さいため、要素数が多いときに有効になります。

binarySearchメソッドの戻り値はリストにkeyが含まれていれば、そのインデックスになります。keyが無いときにはリストの中でkeyが挿入されるべきインデックスの1つ前の値をマイナスにした値になります。例えば、リスト[a, c, e, g, k, m]でhを探索するとします。hはgとkの間に挿入され、gのインデックスは3なので、それをマイナスにした値-3が戻り値になります。

リスト 3 リストの検索の例
    List tmpList = new ArrayList(list);
    Collections.sort(tmpList);
    int index = Collections.binarySearch(tmpList, key);

(2002.09)