オブジェクトの編成方法
表2は、さまざまなオブジェクトの編成方法と、各編成をどのクラスがサポートしているかを示しています。この表の中に2回以上出てくるCollectionクラスがあることに注意してください。このようになっているのは、2つ以上の編成をサポートできるクラスがあるためです。
この表の後で、それぞれの編成について説明します。
編成 | クラス |
リスト(List) | ArrayList |
CopyOnWriteArrayList | |
LinkedList | |
Vector | |
セット(Set) | HashSet |
TreeSet | |
EnumSet | |
LinkedHashSet | |
ConcurrentSkipListSet | |
CopyOnWriteArraySet | |
キュー(Queue) | LinkedList |
PriorityQueue | |
ArrayBlockingQueue | |
ConcurrentLinkedQueue | |
DelayQueue | |
LinkedBlockingQueue | |
PriorityBlockingQueue | |
SynchronousQueue | |
デック(Deque) | ArrayDeque |
LinkedList | |
LinkedBlockingDeque | |
スタック(Stack) | ArrayDeque |
LinkedList | |
LinkedBlockingDeque | |
Stack |
Listクラス
リストは各要素がコレクション内で明示的な位置を持っているコレクションです。List
内の各オブジェクトの位置はオブジェクトの追加順序によって決定できます。オブジェクトをリストに編成しているコレクションクラスはList
インターフェイスを実装しています。
必要とする編成がリストだと判明した場合は、以下の記述を参考にしてクラスを選択してください。
- Vector:
Vector
クラスは最も古いListクラスです。他のListクラスと矛盾するところがいくつかあるのが難点です。このクラスのメソッドはすべて同期化されているため、不必要なオーバーヘッドが生じる可能性があります。一般に新しいコードではVector
クラスを使用すべきではありません。代わりにArrayList
を使用してください。 - ArrayList:
ArrayList
クラスは最も広く使われているListクラスです。ArrayList
クラスの操作の大多数は高速であり、操作に要する時間がリスト内のオブジェクトの数に左右されません。このような高速な操作としては、リストの末尾へのオブジェクトの追加とリストの末尾からのオブジェクトの削除、リストのサイズの取得、リスト内の指定位置にあるオブジェクトの取得、リスト内の指定位置へのオブジェクトの設定が挙げられます。それ以外の操作では、操作に要する時間がリスト内のオブジェクトの数に比例します。 - LinkedList:
LinkedList
クラスの大多数の操作は、ArrayList
クラスのものに比べて長い時間を要します。ただし、LinkedList
クラスでは、リストの途中にオブジェクトを追加したりリストの途中からオブジェクトを削除したりする操作は一定の時間で高速に行われます。ArrayList
クラスでは、これらの操作に要する時間はリスト内で追加位置または削除位置の後ろにあるオブジェクトの数に比例します。これらの操作を多用するのでない限り、一般にはArrayList
を選ぶほうが有利でしょう。LinkedList
クラスはキューやデック(両端キュー)やスタックを編成するのにも便利です。このような柔軟性が必要になることはあまりありませんが、そういう状況ではLinkedList
を選ぶとよいかもしれません。 - CopyOnWriteArrayList:
CopyOnWriteArrayList
クラスは並行処理にかかわる特定の問題をロックに頼らずに解決するのに使われます。イテレータがArrayList
オブジェクトを反復処理していくときに、イテレータから見てArrayList
オブジェクトの一貫性が保たれるようにするためには、ArrayList
オブジェクトの内容が変化してはなりません。そうなれば、イテレータのnext
メソッドまたはprevious
メソッドが次に呼び出されたとき、そのメソッドはConcurrentModificationException
例外を発生させます。このような状況を避ける方法としては、ロックを作成することによって、イテレータがアクティブなうちにArrayList
オブジェクトに変更を加えるメソッドが呼び出されないようにするというやり方もあります。ArrayList
オブジェクトに変更を加えながら反復処理することがめったになければ、ロックを使用するのはよい解決策と言えます。しかし、ArrayList
オブジェクトの反復処理をよく行うのであれば、この問題をロックで解決しようとすると別の問題が発生します。つまり、ArrayList
オブジェクトに変更を加えるためにスレッドの待ち時間が許容できないほど大きくなる可能性があるのです。CopyOnWriteArrayList
クラスの機能はArrayList
クラスに似ていますが、重要な違いが1つあります。CopyOnWriteArrayList
コレクションの内容を変更するすべての操作において、基になっている配列が自分自身のコピーと置き換えられたうえで配列内容が変更されるのです。アクティブなイテレータから見える配列はずっと未変更のままなので、ロックは必要ありません。CopyOnWriteArrayList
オブジェクトによって作成されるイテレータは基になっている配列に変更を加えることができません。これらのイテレータは基になっているコレクションに変更を加えるためのメソッドを持っていますが、それらの実装は基になっているコレクションに変更を加えずにUnsupportedOperationException
を発生させます。
Setクラス
セットは重複のないコレクションです。オブジェクトを追加しようとしたセットに既にそのオブジェクトが含まれていると何も起こりません。オブジェクトをセットに編成しているコレクションクラスはSet
インターフェースを実装しています。
Set
インターフェースを実装したクラスは要素を特定の順序に保つ必要がありません。あるコレクションオブジェクトのクラスでSet
インターフェースが実装されているという以外に、そのオブジェクトについて何もわからなければ、そのコレクションのイテレータでコレクションの内容がどんな順序で反復処理されるのか予測することはできません。
Set
のサブインターフェースとしてSortedSet
というインターフェースがあります。SortedSet
インターフェースを実装しているクラスはセットの要素をソートされた順序に保ちます。
必要とする編成がセットだと判明した場合は、以下の記述を参考にしてクラスを選択してください。
- HashSet:
HashSet
はオブジェクトのセットの管理に最もよく使われるクラスです。HashSet
に対するオブジェクトの追加と削除およびHashSet
内のオブジェクトの検索は高速で行えます。HashSet
のパラメータが適切に調整されていれば、これらの操作の所要時間はセット内のオブジェクトの数に左右されません。HashSet
クラスはセット内のオブジェクトを特定の順序に保ちません。セットの要素を何らかの順序でソートされた状態に保つ必要がある場合は、TreeSet
クラスの使用を検討してください。 - TreeSet:
TreeSet
クラスはセットの要素を指定の順序でソートされた状態に保ちます。しかし、セットに対する要素の追加/削除またはセット内の要素の検索に要する時間は、HashSet
で管理されるセットに比べて一般に長くなります。これらの操作の所要時間はセット内の要素の数の対数に比例します。 - EnumSet:
EnumSet
クラスはenumオブジェクトのセットを表現する高速でコンパクトな方法を提供します。EnumSet
セットのすべての要素は同じenumタイプのものでなければなりません。また、それらの要素は常に自然な順序でソートされます。 - LinkedHashSet:
LinkedHashSet
クラスはHashSet
クラスに似ていますが、要素がセットに挿入された順序と同じ順序に保たれるという点が異なります。LinkedHashSet
オブジェクトに対する要素の追加と削除に要する時間は、HashSet
オブジェクトに比べて少し長くなります。なぜなら、要素の順序を保つためのオーバーヘッドが加わるからです。 - ConcurrentSkipListSet:
ConcurrentSkipListSet
クラスで管理されるセットに対しては、ロックを使用しなくても、同時に複数のスレッドが安全に変更を加えることができます。他のSetクラスで管理されるセットの場合、複数のスレッドで更新される可能性があるなら、複数のスレッドで同時に更新されないようにするためにロックが必要になります。ConcurrentSkipListSet
クラスを使用すれば、スレッドがロックを待つ必要がなくなります。ConcurrentSkipListSet
オブジェクトはセットの要素を指定の順序でソートされた状態に保ちます。ConcurrentSkipListSet
のセットに対する要素の追加/削除またはセット内の要素の検索に要する時間は、TreeSet
と同様、セット内の要素の数の対数に比例します。ただし、これらの操作に要する時間はConcurrentSkipListSet
の方が長くなります。特に長い時間がかかる操作はsize
です。この操作に要する時間はセット内の要素の数に比例します。 - CopyOnWriteArraySet:
CopyOnWriteArraySet
クラスは並行処理にかかわる特定の問題をロックに頼らずに解決するのに使われます。イテレータがHashSet
オブジェクトまたはTreeSet
オブジェクトを反復処理していくときに、イテレータから見てそのセットの一貫性が保たれるようにするためには、セットの内容が変化してはなりません。そうなれば、イテレータのnext
メソッドまたはprevious
メソッドが次に呼び出されたとき、そのメソッドはConcurrentModificationException
例外を発生させます。このような状況を避ける方法としては、ロックを作成することによって、イテレータがアクティブなうちにHashSet
オブジェクトまたはTreeSet
オブジェクトに変更を加えるメソッドが呼び出されないようにするというやり方もあります。セットに変更を加えながら反復処理することがめったになければ、ロックを使用するのはよい解決策と言えます。しかし、セットの反復処理をよく行うのであれば、この問題をロックで解決しようとすると別の問題が発生します。つまり、セットに変更を加えるためにスレッドの待ち時間が許容できないほど大きくなる可能性があるのです。CopyOnWriteArraySet
クラスには他のクラスと違う特徴が1つあります。CopyOnWriteArraySet
コレクションの内容を変更するすべての操作において、基になっている配列が自分自身のコピーと置き換えられたうえで配列内容が変更されるのです。アクティブなイテレータから見える配列はずっと未変更のままなので、ロックは必要ありません。CopyOnWriteArraySet
オブジェクトによって作成されるイテレータは基になっている配列に変更を加えることができません。これらのイテレータは基になっているコレクションに変更を加えるためのメソッドを持っていますが、それらの実装は基になっているコレクションに変更を加えずにUnsupportedOperationException
を発生させます。