Java コレクションフレームワーク

Java 2 よりはるか以前から、Java にはアドホックなクラスが用意されていました。例えば Dictionary、Vector、Stack、Properties などのクラスは、オブジェクトのグループを格納・操作するために使われていました。

これらのクラスはどれも非常に有用ですが、中核となる統一的なテーマが欠けていました。そのため、Vector クラスの使い方と Properties クラスの使い方は大きく異なっていました。

コレクションフレームワークは、次のいくつかの目標を満たすように設計されています。

  • このフレームワークは高性能でなければなりません。基本コレクション(動的配列、リンクリスト、ツリー、ハッシュテーブル)の実装も効率的でなければなりません。

  • このフレームワークは、異なる種類のコレクションが同様の方法で動作し、高度な相互運用性を持つことを可能にします。

  • コレクションの拡張と適応は簡単でなければなりません。

このために、コレクションフレームワーク全体は一連の標準インターフェースを中心に設計されています。これらのインターフェースの標準実装を直接使用できます。たとえば、次のようなものです:LinkedList, HashSet、およびTreeSetなどです。これ以外にも、これらのインターフェースを通じて独自のコレクションを実装することもできます。

上記のコレクションフレームワークの図からわかるように、Java コレクションフレームワークは主に2種類のコンテナを含みます。1つはコレクション(Collection)で、要素の集まりを格納し、もう1つはマップ(Map)で、キー/値ペアのマッピングを格納します。Collection インターフェースにはさらに List、Set、Queue の3つのサブタイプがあり、その下に抽象クラス、最後に具体的な実装クラスがあります。一般的なものにはArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap などがあります。

コレクションフレームワークは、コレクションを表現および操作するための統一アーキテクチャです。すべてのコレクションフレームワークには、次の内容が含まれます:

  • インターフェース:

    インターフェースは、コレクションを表す抽象データ型です。例:Collection、List、Set、Map など。複数のインターフェースを定義するのは、コレクションオブジェクトをさまざまな方法で操作するためです。
  • 実装(クラス):コレクションインターフェースの具体的な実装です。本質的には、再利用可能なデータ構造です。例:ArrayList、LinkedList、HashSet、HashMap。

  • アルゴリズム:コレクションインターフェースを実装したオブジェクト内のメソッドが実行する有用な計算です。例:検索や並べ替え。これらのアルゴリズムは多態性(ポリモーフィズム)を実現します。なぜなら、同じメソッドを類似のインターフェース上で異なる実装として持つことができるからです。

コレクションに加えて、このフレームワークはいくつかの Map インターフェースとクラスも定義しています。Map にはキー/値ペアが格納されます。Map はコレクションではありませんが、コレクションの中に完全に統合されています。

コレクションフレームワークの体系を次の図に示します。

Java コレクションフレームワークは、性能が優れ、使いやすいインターフェースとクラスのセットを提供します。Java コレクションフレームワークは java.util パッケージに含まれているため、コレクションフレームワークを使用するときはパッケージのインポートが必要です。


コレクションインターフェース

コレクションフレームワークはいくつかのインターフェースを定義しています。このセクションでは、各インターフェースの概要を説明します:

番号 インターフェースの説明
1 Collection インターフェース

Collection は最も基本的なコレクションインターフェースです。Collection は Object のグループ、すなわち Collection の要素を表します。Java は Collection を直接継承するクラスを提供せず、Collection を継承したサブインターフェース(List や Set など)のみを提供します。

Collection インターフェースは、一意でなく、順序のないオブジェクトのグループを格納します。

2 List インターフェース

List インターフェースは順序付けられた Collection です。このインターフェースを使用すると、各要素の挿入位置を正確に制御でき、インデックス(List 内での要素の位置。配列の添字に類似)によって List 内の要素にアクセスできます。最初の要素のインデックスは 0 で、同じ要素を持つことも許可されます。

List インターフェースは、一意でなく、順序付けられた(挿入順の)オブジェクトのグループを格納します。

3 Set

Set は Collection とまったく同じインターフェースを持ちますが、動作が異なります。Set は重複する要素を保持しません。

Set インターフェースは、一意で、順序のないオブジェクトのグループを格納します。

4 SortedSet
Set を継承し、ソートされたコレクションを保持します。
5 Map

Map インターフェースはキーと値のオブジェクトのグループを格納し、キーから値へのマッピングを提供します。

6 Map.Entry
Map 内の1つの要素(キー/値ペア)を表します。Map の内部インターフェースです。
7 SortedMap
Map を継承し、キーを昇順に保持します。
8 Enumeration
これは従来のインターフェースと定義されたメソッドで、これによりオブジェクトコレクション内の要素を列挙(一度に1つずつ取得)できます。この従来のインターフェースはイテレータに置き換えられました。

Set と List の違い

  • 1. Set インターフェースのインスタンスは、順序のない重複しないデータを格納します。List インターフェースのインスタンスは、順序付けられた重複可能な要素を格納します。

  • 2. Set は検索効率が低く、削除と挿入の効率が高いです。挿入と削除は要素の位置の変更を引き起こしません。<実装クラスには HashSet, TreeSet があります>。

  • 3. List は配列に似ており、動的に成長できます。実際に格納されたデータの長さに応じて List の長さを自動的に増やします。要素の検索効率は高いですが、他の要素の位置の変更を引き起こすため、挿入・削除の効率は低いです。<実装クラスには ArrayList, LinkedList, Vector があります> 。


コレクション実装クラス(コレクションクラス)

Java は Collection インターフェースを実装した標準コレクションクラスのセットを提供します。その一部は具象クラスで、直接使用できます。また別の一部は抽象クラスで、インターフェースの部分実装を提供します。

標準コレクションクラスを以下の表にまとめます:

番号 クラスの説明
1 AbstractCollection 
コレクションインターフェースの大部分を実装します。
2 AbstractList 
AbstractCollection を継承し、List インターフェースの大部分を実装します。
3 AbstractSequentialList 
AbstractList を継承し、ランダムアクセスではなく、データ要素へのチェーンアクセス(連鎖アクセス)を提供します。
4 LinkedList

このクラスは List インターフェースを実装し、null(空)要素を許可します。主にリンクリストのデータ構造を作成するために使用されます。このクラスには同期メソッドがないため、複数のスレッドが同時に List にアクセスする場合は、アクセス同期を自分で実装する必要があります。解決策は、List を作成するときに同期された List を構築することです。例:

List list=Collections.synchronizedList(newLinkedList(...));

LinkedList は検索効率が低いです。

5 ArrayList

このクラスも List インターフェースを実装しており、可変サイズの配列を実装し、ランダムアクセスと要素の走査においてより良い性能を提供します。このクラスも非同期であり、マルチスレッドの状況では使用しないでください。ArrayList は現在の長さの50%ずつ増加し、挿入・削除の効率は低いです。

6 AbstractSet 
AbstractCollection を継承し、Set インターフェースの大部分を実装します。
7 HashSet

このクラスは Set インターフェースを実装し、重複要素を許可しません。コレクション内の要素の順序は保証されません。値が null の要素を含むことはできますが、最大1つまでです。

8 LinkedHashSet
予測可能な反復順序を持つSetインターフェースのハッシュテーブルとリンクリスト実装。
9 TreeSet

このクラスは Set インターフェースを実装し、ソートなどの機能を実現できます。

10 AbstractMap 
Map インターフェースの大部分を実装します。
11 HashMap
HashMap はハッシュテーブルであり、キーと値のペア(key-value)のマッピングを格納します。
このクラスは Map インターフェースを実装し、キーの HashCode 値に基づいてデータを格納するため、非常に高速なアクセス速度を持ちます。最大で1つのレコードのキーが null になることを許可しますが、スレッド同期はサポートしていません。
12 TreeMap
AbstractMap を継承し、ツリーを使用します。
13 WeakHashMap
AbstractMap クラスを継承し、弱いキーを使用するハッシュテーブルを使用します。
14 LinkedHashMap
HashMap を継承し、要素の自然順序を使用して要素をソートします。
15 IdentityHashMap
AbstractMap クラスを継承し、オブジェクトを比較するときに参照の等価性を使用します。

以前のチュートリアルでは、java.util パッケージで定義された次のようなクラスについて説明しました。

番号 クラスの説明
1 Vector

このクラスは ArrayList と非常によく似ていますが、このクラスは同期されており、マルチスレッドの状況で使用できます。このクラスはデフォルトの増加長を設定でき、デフォルトの拡張方法は元のサイズの2倍です。

2 Stack
Stack は Vector のサブクラスであり、標準的な後入れ先出し(LIFO)スタックを実装しています。
3 Dictionary
Dictionary クラスは抽象クラスで、キー/値ペアを格納するために使用され、Map クラスと同様の働きをします。
4 Hashtable

Hashtable は Dictionary(辞書)クラスのサブクラスであり、java.util パッケージにあります。

5 Properties
Properties は Hashtable を継承し、永続的なプロパティセットを表します。プロパティリスト内の各キーと対応する値はすべて文字列です。
6 BitSet
BitSet クラスは、ビット値を保持する特別な種類の配列を作成します。BitSet 内の配列サイズは必要に応じて増加します。

コレクションアルゴリズム

コレクションフレームワークは、コレクションとマップに使用できるいくつかのアルゴリズムを定義しています。これらのアルゴリズムは、コレクションクラスの静的メソッドとして定義されています。

互換性のない型を比較しようとすると、一部のメソッドは ClassCastException 例外をスローすることがあります。変更不可能なコレクションを変更しようとすると、UnsupportedOperationException 例外がスローされます。

コレクションは3つの静的な変数、EMPTY_SET、EMPTY_LIST、EMPTY_MAPを定義しています。これらの変数はすべて変更できません。

番号 アルゴリズムの説明
1 コレクションのアルゴリズム(Collection Algorithms)
ここにすべてのアルゴリズム実装のリストがあります。

イテレータの使い方

通常、コレクション内の要素を走査したいと思うでしょう。例えば、コレクションの各要素を表示する場合です。

一般に、配列の走査にはforループまたは拡張forを使用します。これらの2つの方法はコレクションフレームワークでも使用できますが、もう1つの方法として、イテレータを使用してコレクションフレームワークを走査する方法もあります。それはオブジェクトであり、実装しているのはIteratorインターフェースまたは ListIterator インターフェースです。

イテレータを使用すると、ループによってコレクションの要素を取得または削除できます。ListIterator は Iterator を継承しており、リストの双方向走査と要素の変更を可能にします。

番号 イテレータメソッドの説明
1 Java Iterator の使用
ここでは、Iterator および ListIterator インターフェースが提供するすべてのメソッドをインスタンスで列挙します。

ArrayList の走査

例

import java.util.*; public class Test{ public static void main(String[] args) { List<String> list=new ArrayList<String>(); list.add("Hello"); list.add("World"); list.add("HAHAHAHA"); //最初の走査方法は For-Each を使用して List を走査するものです。 for (String str : list) { //for(int i=0;i<list.size();i++) という形式に書き換えることもできます。 System.out.println(str); } //2番目の走査は、リストを配列に変換して走査するものです。 String[] strArray=new String[list.size()]; list.toArray(strArray); for(int i=0;i<strArray.length;i++) //ここも for(String str:strArray) という形式に書き換えることができます。 { System.out.println(strArray[i]); } //3番目の走査は、イテレータを使用して走査します。 Iterator<String> ite=list.iterator(); while(ite.hasNext())//次の要素が存在するかどうかを判定します。 { System.out.println(ite.next()); } } }

解説:

3つの方法はすべて ArrayList コレクションを走査するためのものです。3番目の方法はイテレータを使用する方法で、走査中にコレクションの長さを超えることを心配する必要がありません。

Map の走査

例

import java.util.*; public class Test{ public static void main(String[] args) { Map<String, String> map = new HashMap<String, String>(); map.put("1", "value1"); map.put("2", "value2"); map.put("3", "value3"); //最初の方法:一般的に使用され、2回に分けて値を取得します。 System.out.println("Map.keySet を使用して key と value を走査します:"); for (String key : map.keySet()) { System.out.println("key= "+ key + " and value= " + map.get(key)); } //2番目の方法 System.out.println("Map.entrySet と iterator を使用して key と value を走査します:"); Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, String> entry = it.next(); System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } //3番目の方法:推奨されます。特に容量が大きい場合に適しています。 System.out.println("Map.entrySet を使用して key と value を走査します。"); for (Map.Entry<String, String> entry : map.entrySet()) { System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } //4番目の方法 System.out.println("Map.values() を使用してすべての value を走査しますが、key は走査できません。"); for (String v : map.values()) { System.out.println("value= " + v); } } }


コンパレータの使い方

TreeSet と TreeMap は、ソート順に要素を格納します。しかし、どのようなソート順にするかは、コンパレータによって正確に定義されます。

このインターフェースを使用すると、コレクションをさまざまな方法でソートできます。

番号 コンパレータメソッドの説明
1 Java Comparator の使用
ここでは、Comparator インターフェースが提供するすべてのメソッドをインスタンスで列挙します。

まとめ

Java コレクションフレームワークは、プログラマーに、それらを操作するための事前にパッケージ化されたデータ構造とアルゴリズムを提供します。

コレクションは、他のオブジェクトへの参照を保持できるオブジェクトです。コレクションインターフェースは、各タイプのコレクションに対して実行できる操作を宣言します。

コレクションフレームワークのクラスとインターフェースはすべて java.util パッケージにあります。

オブジェクトをコレクションクラスに追加すると、自動的に Object 型に変換されるため、取り出すときに強制型変換を行う必要があります。

その他の拡張