Revised: May/6th/2008: Since: Feb./23rd/2003
複数の要素からなるデータの集まりをコレクションと呼びます。コレクションは各々、リスト、セット、ツリー、ハッシュテーブルなどのデータ構造を持ちます。コレクション・フレームワークは J2SDK 1.2 で導入されたもので、JDK 1.2 未満で使われていたコレクション API の後継です。それまでの遅いコレクション・クラスを完全に新しくしたもので、インタフェースをベースに全体が設計されており、これを使うためには、共通のインタフェース・メソッドにのっとることになります。このような全体をフレームワークと呼びます。J2SDK 1.2 で、従来からあるコレクション・クラスも、フレームワークに含まれるものとして再設計されました。
万能のアルゴリズム/データ構造はありません。要件に応じたコレクション・クラスを使いこなすためには、要件の明確化とそれぞれのクラスの特性を理解することが重要です。ここではJavaのコレクション・フレームワークの詳細は解説しませんが、全体を概観してみましょう。
コレクション・フレームワークは、3つのインタフェース、java.util.List, java.util.Set, java.util.Mapと、その実装クラスで構成されています。JDK 5.0 からは java.util.Queue が追加されました。
List, Set, Queue は Collection インタフェースを継承しています。List はシーケンシャル(直線的)なデータ構造を表します。Set は要素の重複を許さない集合を表します。Queue はキュー(待ち行列)を表します。Map はキーとデータの組からなるハッシュテーブルのデータ構造です。
一般に、プログラミングで用いるデータ構造は、次のいずれかに分類されることがほとんどです。
次の図は、コレクション・フレームワークのjava.utilパッケージにおける、代表的なインタフェース/クラスの階層図です。java.util.*のクラス/インタフェースと、java.util.concurrent.*のクラス/インタフェースが混じっています。
![]() |
| 図: J2SE 5.0 コレクション・フレームワークのクラス図 |
|---|
Vector, Stack, Hashtable は、Java 2以前から存在し、今でも広く使われています。それぞれ、可変長配列、スタック、ハッシュテーブルを実装します。Java 2では、これらの古いクラスも、コレクション・フレームワークの枠組みで改良されています。
古いコレクション・クラスには、処理速度が遅いという問題がありました。その最大の原因は、オブジェクトが常に「同期化」されることです。同期化とは、1つのオブジェクトが同時に最大でも1つのスレッドだけから更新されるように実装することです。データ整合性を確保するために、同期化の仕組みは不可欠である一方で、オブジェクトの生成/破棄と並び、Javaプログラムの実行速度を下げるものの代表格でもあります。
Java 2で導入されたコレクション・フレームワークのクラスは、同期化されないものとして設計されました。現在では、複数スレッド間で競合がない場合は、同期化されたコードによる無用なパフォーマンス上の劣化はほとんどありませんが、必要なときだけ同期化するという怠惰(lazy)な設計思想が踏襲されています。
以下、Java SE 6の各インタフェースの概要です。
java.util.Listインタフェース要素は順序を持ち、索引によってアクセスされます。セットとは異なり、同じ値を重複して持つことが可能です。配列と同様、索引は0から始まります。add(), remove(), indexOf(), get()など、実装クラスで利用される殆どのメソッドを宣言しています。
ArrayListクラスVectorの後継。インタフェースLinkの基本的な実装クラスです。リサイズ可能な配列を実装します。volatileの問題として処理します。一方、エントリーの増減を伴うような場合は、インスタンス化した後で、Collections.synchronizedListメソッドを使用して「同期化されたリスト・オブジェクト」を生成します。当該リストをカプセル化するオブジェクトが既にある場合は、当該オブジェクトで同期化しても同じです。LinkedListクラスArrayListと同じです。J2SE 5.0以上でインタフェースQueueの実装メソッドが、Java SE 6以上で、インタフェースDequeの実装メソッドが追加されました。VectorクラスArrayListかLinkedListクラスを用いることが推奨されます。StackクラスVectorを継承し、push(), pop()などのメソッドが追加されています。Java SE 6.0以上では、Dequeインタフェースの実装クラスが推奨されています。java.util.Mapインタフェースキーと値の組を一つのエントリーとして持ちます。値へはキーによってアクセスされます。put(), get(), remove()などのメソッドを宣言しています。抽象クラスDictionaryの後継として作成されました。
HashMapクラスHashtableの後継。インタフェースMapの基本的な実装クラスです。ハッシュテーブルを実装します。volatileの問題として処理します。一方、インスタンス化した後で、Collections.synchronizedMapメソッドを使用して「同期化されたリスト・オブジェクト」を生成します。当該リストをカプセル化するオブジェクトが既にある場合は、当該オブジェクトで同期化しても同じです。LinkedHashMapクラスHashMapと同じです。TreeMapクラスComparatorの実装クラスで指定します。大量なエントリを保持し、パフォーマンスが重要な場合は、TreeMapの利用を検討すべきです。同期に関する注意点はHashMapと同じです。EnumMapクラスEnumクラスをキーの持てるマップ。同期に関する注意点はHashMapと同じです。WeakHashMapクラスWeakHashMapオブジェクト自身は持続していても、キーを明示的に参照するスレッドがなくなると、当該キーが占有していたメモリ領域が再利用可能となります。内部管理上のオーバーヘッドと引き換えにメモリ効率が向上します。当該ハッシュから、そのキーのエントリーがなくなるので、持続的なデータの保持のためではなく、レジストリ、キャッシュのように利用します。同期に関する注意点はHashMapと同じです。java.lang.ref.WeakReference型オブジェクトであり、オブジェクトを破棄してメモリを再利用するために内部的に実行されているガベッジ・コレクタを、間接的に操作する仕組みです。詳細は次のURLを参照してください:http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/ref/package-summary.html#reachabilityHashtableクラスDictionaryを継承します。同期の用件が無ければ、クラスHashMapを用いることが推奨されます。PropertiesクラスHashtableを継承します。A=B形式のプロパティの列挙、プロパティ・ファイルの読み込み/書き出しのために使います。ISO-8859-1 (Latin-1)以外の文字は、native2asciiツールなどを用いてUNICODEエスケープする必要があります。プロパティ・ファイルの読み書きには、java.util.ResourceBundle、java.util.prefs.Preferencesなどの利用も検討してください。java.util.Setインタフェース重複要素のない集合を表現します。このインタフェースの実装クラスは、内部的にはMapインタフェースの実装クラスに基づいており、動作特性も共通しています。
HashSetクラスSetの基本的な実装クラスです。集合を実装します。volatileの問題として処理します。一方、インスタンス化した後で、Collections.synchronizedSetメソッドを使用して「同期化されたリスト・オブジェクト」を生成します。当該リストをカプセル化するオブジェクトが既にある場合は、当該オブジェクトで同期化しても同じです。LinkedHashSetクラスHashSetと同じです。TreeSetクラスHashSetと同じです。順序は、自然順序付けかComparatorの実装クラスで指定します。大量なエントリを保持し、パフォーマンスが重要な場合は、TreeSetの利用を検討すべきです。同期に関する注意点はHashSetと同じです。EnumSetクラスEnumクラスを持てるセット。同期に関する注意点はHashMapと同じです。java.util.Queueインタフェース待ち行列を表現します。最も基本的な実装クラスはLinkedListです。Comparatorで明示的に順序付けできるPriorityQueueや、スレッドセーフなConcurrentLinkedQueueなどの他、以下のサブインタフェースの実装クラスなど多数あります。
サブインタフェースjava.util.Dequeは、両端で要素を挿入/削除する機能を追加します。実装クラスには、LinkedListのほか、ArrayDeque, LinkedBlockingDequeがあります。
サブインタフェースjava.util.concurrent.BlockingQueueは、要素の追加時にキュー内に空きが生じるまで待機したり、要素の取得時にキュー要素が追加されるまで待機したりする機能を追加します。Dequeをスーパーインタフェースとするjava.util.concurrent.BlockingDequeインタフェースもあります。実装クラスには、ArrayBlockingQueue, LinkedBlockingQueue, LinkedBlockingDequeなどがあります。
Java SE 6までのコレクション全般に渡る情報として、更に次のことを補足します:
Object型で出し入れします。J2SE 5.0 (Tiger)以降は、ジェネリクスという言語仕様でインスタンス化時に「型パラメータ」を指定できるようになりました。型パラメタを使用してインスタンス化すれば、取り出した要素を使うために、目的の型へキャストする必要がなくなります。VectorやHashtableなどの同期がとられるスレッド・セーフなオブジェクトはパフォーマンスが悪く、Java 2で追加されたコレクション・クラスでは同期がとられないようになっています。VectorやHashtableなどの古いクラスでは、後方互換のために、Java 2以前のAPIを実装しています。また、要素を順番に取り出すために、EnumerationとIteratorの両方が使えますが、新しいクラスではIteratorだけしか使えません。J2SE 5.0 (Tiger)以降では、コレクションクラスで型パラメータを指定した場合、イテレータでも型パラメータを指定しておくべきです。また、新しく導入されたfor/in構造を用いれば、イテレータは殆ど使わなくて済みます。java.util.Collectionsは、コレクションを操作するユーティリティ・クラスで、スレッド・セーフなオブジェクトや不変オブジェクトの生成などが可能です。J2SE 5.0で多数の便利なメソッドが追加されています。java.util.Arraysも、クラスCollectionsと同様、配列を操作するユーティリティ・クラスであり、検索、ソート、比較などが可能です。J2SE 5.0で多数の便利なメソッドが追加されています。