コレクション

Javaでは「コレクション」を「オブジェクトのグループを表すオブジェクト」とし、これらコレクションの取扱いを統一するためのアーキテクチャとして「コレクション・フレームワーク」があります。


JDKドキュメントでは「コレクション・フレームワーク」の利点を次のように記しています。

  • プログラミングの労力の軽減
  • パフォーマンスの向上
  • 関連性のない API 間の相互運用が可能
  • API の学習に必要な労力を軽減
  • API の設計と実装に必要な労力を軽減
  • ソフトウェアの再利用を促進

ここでは、「コレクション・フレームワーク」の主要要素である次のコレクションインターフェースおよびクラスをみていきます。


「コレクション・フレームワーク」のクラス構成は次のようになっています。

コレクションのクラス構成

Set

「Set」は重複要素の無いコレクションになります。主な実装クラスとして次のものがあります。


Set系クラス一覧
クラス 特徴
HashSet 「Set」の中で一番高速に処理します。要素の追加順序を保障しません。null要素を許容します。スレッドに対し同期化されません。
LinkedHashSet 要素の追加順序を保障します。null要素を許容します。スレッドに対し同期化されません。
TreeSet 追加された要素をソートします。ソート順序は「自然順序付け」と呼ばれる「java.lang.Comparable」インタフェースの「compare」メソッドによって定義される順序に従っています。従って、このインタフェースを実装した独自のクラスを用意し「compare」メソッドを定義することでソート順序を変更することができます。null要素を許容しません。スレッドに対し同期化されません。

各クラスの詳細はJDK API仕様を参照してください。


書式

Set系のクラスも他のクラスと同様にインスタンスを生成し使用しますが、「JDK 5.0」以降からは「Generics」という機能が追加されインスタンス生成時に要素の型も宣言するようになりました。例として「HashSet」の場合の書式は次のようになります。

HashSet<要素の型> 変数 = new HashSet<要素の型>();

・サンプルソース(Sample1401.java)

import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;

// このクラスで「compare」メソッドを定義する為、「Comparator」インタフェースを実装しています。
public class Sample1401 implements Comparator<String> {
    private static String[] animal = {"Cat", "Dog", "Frog", "Duck"};

    public static void main(String[] args) {
        testHashSet();       //「HashSet」の動作確認処理の呼び出します。
        testLinkedHashSet(); //「LinkedHashSet」の動作確認処理の呼び出します。
        testTreeSet();       //「TreeSet」の動作確認処理の呼び出します。
    }

    //「HashSet」の動作確認処理
    private static void testHashSet() {
        HashSet<String> hs = new HashSet<String>();
        for(int i=0; i<animal.length; i++) {
            hs.add(animal[i]);
        }
        hs.add("Cat"); // 重複する要素を追加します。
        hs.add(null);  // nullを追加します。
        System.out.println("HashSet:" + hs);
    }

    //「LinkedHashSet」の動作確認処理
    private static void testLinkedHashSet() {
        LinkedHashSet<String> lhs = new LinkedHashSet<String>();
        for(int i=0; i<animal.length; i++) {
            lhs.add(animal[i]);
        }
        lhs.add("Cat"); // 重複する要素を追加します。
        lhs.add(null);  // nullを追加します。
        System.out.println("LinkedHashSet:" + lhs);
    }

    //「TreeSet」の動作確認処理
    private static void testTreeSet() {
        TreeSet<String> ts1 = new TreeSet<String>();
        // ソート順序を変更した「Comparator」クラスの実装を引数に指定します。
        TreeSet<String> ts2 = new TreeSet<String>(new Sample1401());
        for(int i=0; i<animal.length; i++) {
            ts1.add(animal[i]);
            ts2.add(animal[i]);
        }
        ts1.add("Cat");   // 重複する要素を追加します。
        // ts1.add(null); // nullを追加すると例外(ソート時にNullPointerException)が発生します。
        System.out.println("TreeSet(デフォルト):" + ts1);
        System.out.println("TreeSet(降順に変更):" + ts2);
    }

    //「compare」の定義(ソート順序を降順に変更)
    public int compare(String s1, String s2) {
        return s1.compareTo(s2)*-1;
    }
}

・実行結果

C:\dev\java>javac Sample1401.java [Enter]

C:\dev\java>java Sample1401 [Enter]
HashSet:[Cat, null, Frog, Duck, Dog]
LinkedHashSet:[Cat, Dog, Frog, Duck, null]
TreeSet(デフォルト):[Cat, Dog, Duck, Frog]
TreeSet(降順に変更):[Frog, Duck, Dog, Cat]
        

▲PageTop

List

「List」は順序付けされたコレクションになります。主な実装クラスとして次のものがあります。


List系クラス一覧
クラス 特徴
ArryList 「List」の中で最も基本的な実装です。nullを含む全ての要素を保障します。要素はリスト指定した位置または最後の位置に挿入することができ、挿入した位置を指定して要素を取り出すことができます。要素を配列で保持しているため要素の取得は高速に処理できますが、要素数が多くなると追加や削除処理に時間が掛かります。スレッドに対し同期化されません。
LinkedList 「ArrayList」と基本操作は同じですが、「ArrayList」は要素を配列として保持しているのに対して「LinkedList」は要素をリスト構造を用いて管理しています。これにより要素の追加や削除作業が全体の要素に影響することなく行えるため高速に処理できます。しかし要素の検索はリスト構造の先端または終端から順に探していくことになるのでランダムアクセスには不向きです。このためスタックやキューのような処理に使用されます。スレッドに対し同期化されません。

各クラスの詳細はJDK API仕様を参照してください。


書式

list系のクラスも他のクラスと同様にインスタンスを生成し使用しますが、「JDK 5.0」以降からは「Generics」という機能が追加されインスタンス生成時に要素の型も宣言するようになりました。例として「ArrayList」の場合の書式は次のようになります。

ArrayList<要素の型> 変数 = new ArrayList<要素の型>();

・サンプルソース(Sample1402.java)

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class Sample1402 {
    private static int dataCount = 20000;

    public static void main(String[] args) {
        ArrayList<Integer> arraylist = new ArrayList<Integer>();
        LinkedList<Integer> linkedlist = new LinkedList<Integer>();
        System.out.println("追加処理の比較---------------------------------------");
        System.out.println(" ArrayList = " + getAddTime(arraylist));
        System.out.println(" LinkedList= " + getAddTime(linkedlist));
        System.out.println("検索処理の比較---------------------------------------");
        System.out.println(" ArrayList(forループによるget()を使用)  = " + getGetTime(arraylist, false));
        System.out.println(" LinkedList(forループによるget()を使用)= " + getGetTime(linkedlist, false));
        System.out.println(" ArrayList(Iteratorを使用) = " + getGetTime(arraylist, true));
        System.out.println(" LinkedList(Iteratorを使用)= " + getGetTime(linkedlist, true));
        System.out.println("更新処理の比較---------------------------------------");
        System.out.println(" ArrayList(先頭の要素) = " + getUpTime(arraylist, 0));
        System.out.println(" LinkedList(先頭の要素)= " + getUpTime(linkedlist, 0));
        System.out.println(" ArrayList(中間の要素) = " + getUpTime(arraylist, dataCount/2));
        System.out.println(" LinkedList(中間の要素)= " + getUpTime(linkedlist, dataCount/2));
        System.out.println("削除処理の比較---------------------------------------");
        System.out.println(" ArrayList = " + getDelTime(arraylist));
        System.out.println(" LinkedList= " + getDelTime(linkedlist));
    }

    private static String getAddTime(List<Integer> list) {
        long startTime = System.currentTimeMillis();
        for(int i=0; i<dataCount; i++) {
            list.add(i);
        }
        long endTime = System.currentTimeMillis();
        return (endTime - startTime) + " msec";
    }

    private static String getGetTime(List<Integer> list, boolean iteratorFlg) {
        long startTime,endTime;
        if (iteratorFlg) {
            startTime = System.currentTimeMillis();
            for(Iterator<Integer> iterator=list.iterator();iterator.hasNext();){
                iterator.next();
            }
            endTime = System.currentTimeMillis();
        }else {
            startTime = System.currentTimeMillis();
            for(int i=0; i<dataCount; i++){
                list.get(i);
            }
            endTime = System.currentTimeMillis();
        }
        return (endTime - startTime) + " msec";
    }

    private static String getUpTime(List<Integer> list, int index) {
        long startTime = System.currentTimeMillis();
        for(int i=0; i<dataCount; i++) {
            list.add(index, i);
        }
        long endTime = System.currentTimeMillis();
        return (endTime - startTime) + " msec";
    }

    private static String getDelTime(List<Integer> list) {
        long startTime = System.currentTimeMillis();
        while(!list.isEmpty()) {
              list.remove(0);
        }
        long endTime = System.currentTimeMillis();
        return (endTime - startTime) + " msec";
    }
}

・実行結果

C:\dev\java>javac Sample1402.java [Enter]

C:\dev\java>java Sample1402 [Enter]
追加処理の比較---------------------------------------
 ArrayList = 16 msec
 LinkedList= 15 msec
検索処理の比較---------------------------------------
 ArrayList(forループによるget()を使用)  = 0 msec
 LinkedList(forループによるget()を使用)= 688 msec
 ArrayList(Iteratorを使用) = 0 msec
 LinkedList(Iteratorを使用)= 0 msec
更新処理の比較---------------------------------------
 ArrayList(先頭の要素) = 812 msec
 LinkedList(先頭の要素)= 16 msec
 ArrayList(中間の要素) = 1031 msec
 LinkedList(中間の要素)= 1500 msec
削除処理の比較---------------------------------------
 ArrayList = 2281 msec
 LinkedList= 0 msec
        

▲PageTop

Map

「Map」はキーと要素をマッピングしたコレクションになります。主な実装クラスとして次のものがあります。


Map系クラス一覧
クラス 説明
HashMap 「Map」の中で最も基本的な実装です。キー及び値にnullを使用することが出来ます。スレッドに対し同期化されません。
LinkedHashMap マップに挿入したキーの順序を保障します。スレッドに対し同期化されません。
TreeMap マップ内でキーを昇順にソートします。スレッドに対し同期化されません。

各クラスの詳細はJDK API仕様を参照してください。


書式

Map系のクラスも他のクラスと同様にインスタンスを生成し使用しますが、「JDK 5.0」以降からは「Generics」という機能が追加されインスタンス生成時に要素の型も宣言するようになりました。例として「HashMap」の場合の書式は次のようになります。

HashMap<要素の型> 変数 = new HashMap<要素の型>();

・サンプルソース(Sample1403.java)

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.TreeMap;

public class Sample1403 {
    public static void main(String[] args) {
        String[] array = {"ねこ", "いぬ", "かえる", "あひる"};
        HashMap<String, String> hashmap = new HashMap<String, String>();
        LinkedHashMap<String, String> linkedhashmap = new LinkedHashMap<String, String>();
        TreeMap<String, String> treemap = new TreeMap<String, String>();

       for(int i=array.length; i>0; i--) {
            hashmap.put(String.valueOf(i), array[i-1]);
            linkedhashmap.put(String.valueOf(i), array[i-1]);
            treemap.put(String.valueOf(i), array[i-1]);
        }

        System.out.println("HashMap : " + hashmap.toString());
        System.out.println("LinkedHashMap : " + linkedhashmap.toString());
        System.out.println("TreeMap : " + treemap.toString());
    }
}

・実行結果

C:\dev\java>javac Sample1403.java [Enter]

C:\dev\java>java Sample1403 [Enter]
HashMap : {3=かえる, 2=いぬ, 4=あひる, 1=ねこ}
LinkedHashMap : {4=あひる, 3=かえる, 2=いぬ, 1=ねこ}
TreeMap : {1=ねこ, 2=いぬ, 3=かえる, 4=あひる}
        

▲PageTop