オブジェクトの編成方法
表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を発生させます。
