9. 配列とコレクション
9.1. 配列
配列は、同じデータ型の変数をメモリ上の連続した領域に確保したものです。配列は通常、1 つの変数に格納できないような大きなデータ (例: ファイル、ネットワークの送受信データ) を保持するために用います。配列を構成する一つひとつの変数を「要素」といい、それぞれの要素には先頭から「添字 (インデックス)」と呼ばれる連番が振られています。配列の各要素に対して配列全体を「配列オブジェクト」と呼ぶこともあります。
Java における配列は、配列全体としてみた場合はクラスに準じた扱いとなり、フィールドとすることや、メソッド・コンストラクタの引数または戻り値として使用することができます。
9.1.1. 配列生成式
配列を生成するための式を「配列生成式」といいます。配列生成式で用いる記号 [ ] を配列演算子と呼び、宣言するものが配列であることと、配列の要素数を表します。
- 書式 (1):
new Type [ n ]Type: 要素型、n: 要素数
- 書式 (2):
new Type [] { [ value [, value ...] ] }Type: 要素型、value: 初期値
- 要素型
Typeは配列の各要素のデータ型を表し、具体的にはプリミティブ型またはクラスとなります。基本データ型と配列演算子[ ]が組み合わさることで配列生成式として認識されます。 - 書式 (1) は、配列の要素数を指定して配列を生成する式です。要素数は
[ ]内に指定された値となり、各要素の初期状態は変数・フィールドの初期値と同じ扱いとなります。- 配列をフィールドの初期値として生成した場合、各要素の初期値はフィールドと同じになります。
- 配列をメソッド内で生成した場合、各要素の初期値はローカル変数同様「不定」であるため、改めて値を代入する必要があります (配列アクセス式を用います)。
- 書式 (2) は、配列の初期値を指定して配列を生成する式です。
{ }内に初期値をカンマ区切りで列挙します (0 個も許されます)。配列の要素数は列挙した初期値の数となります。- 配列演算子
[ ]の中には値を記述しません。 - 配列の各要素が初期化される点では確実ですが、初期値の指定に漏れがあると予期しない要素数の配列が生成されるため注意が必要です。
- 配列演算子
9.1.2. 配列変数宣言文
配列はそれ全体を変数として宣言することができます。
- 書式:
Type [ ] array [= expr] ;Type: 要素型、array: 配列の変数名、expr: 初期値
- 基本データ型
Typeは配列の各要素のデータ型を表し、具体的にはプリミティブ型またはクラスとなります。基本データ型と配列演算子[ ]が組み合わさることで配列の変数宣言文として認識されます。 - 初期値
exprは配列生成式または既存の配列となります。exprが表す配列の基本データ型は、配列の変数宣言文の基本データ型Typeと一致している必要があります。 - 配列の変数宣言文は、ローカル変数宣言文としても、フィールド定義 (初期化を含む) としても使用することができます。
9.1.3. 配列アクセス式
配列アクセス式を使用すると、配列の各要素に対して読み取りまたは書き込みができるようになります。
- 書式:
array [ index ]array: 配列の変数名、index: 添字
- 添字
indexは0から始まり、要素数 - 1 までの間で指定します。添字が負数、または要素数 - 1 を超えた場合には Java VM が非チェック例外ArrayIndexOutOfBoundsExceptionをスローします。Visual Basic と Python では添字の範囲を自由に設定できますが、Java では言語仕様で添字の開始は0、終了は 要素数 - 1 と決められています (C と JavaScript の配列の添字も、Java と同じ仕様です)。 - 変数名と添字の組み合わせにより、基本データ型の変数として扱うことができるようになります。
9.1.4. 配列の length フィールド
Java の配列には length フィールドが組み込まれており、生成された配列の要素数を取得できるようになっています。
- 書式:
array . lengtharray: 配列の変数名
array . lengthは読み取り専用です。array . lengthの値は要素数 (int型) です。このことから、配列の添字には0からarray . length - 1までの範囲の数値 (int型) を使用できることがわかります。- 初期化していない
array . lengthを読み取ろうとすると、Java VM が非チェック例外NullPointerExceptionをスローします。
配列の length フィールドは、配列操作時に添字の範囲を超えないようにするため頻繁に参照されます。また、for 文と組み合わせて配列の要素に連続してアクセスするためにも用いられます。特に他のメソッドで生成された配列は要素数がわからないため、必ず length フィールドで要素数をチェックするようにしましょう。
byte[] array = new byte[256];
for (int index = 0; index < array.length; index++) {
array[index] = (byte) 0;
}
9.1.5. 多次元配列
Java には C、C++ や Visual Basic のような多次元配列を実質的にサポートしません。代わりに配列の要素を配列として宣言することで多次元配列のように用いることができます。
多次元配列は数値演算で使用することはありますが、元々使い勝手が悪く、使用頻度も低いためここでは割愛します (実際に多次元配列が必要な局面では、多次元配列の機能を持つクラスを定義して用いることが多いようです)。
9.2. コレクション
9.2.1. Java Collections Framework とは
Java SE の標準 API には "Java Collections Framework" と呼ばれる、主要なデータ構造と基本的なアルゴリズムをサポートするクラスライブラリが含まれています。他の言語ではプログラマが必要なデータ構造とアルゴリズムを選択して独自に実装しなければならない場合もありますが、Java ではそれらがあらかじめ API として用意されているため、適切なデータ構造とアルゴリズムを選択するだけで済みます。
ここでは、Java Collections Framework で定義されているデータ構造をコレクションと呼びます。コレクション (操作) はインタフェースにて定義されており、それぞれ要素の保持方法などが異なる複数の実装クラスを持っています。実装クラスは通常、標準で用意されているものだけでも十分ですが、必要に応じて独自に定義することもできます。
コレクションとその実装は java.util パッケージに集約されています。以下に基本的かつ使用頻度の高いコレクションを示します。
Collection: コレクションのスーパーインタフェース。順序、要素の重複およびアクセス方法はサブインタフェースが決定する。Set: 集合。順序を持たず要素の重複も許さない。List: リスト。順序を持ち要素の重複も許す、配列同様に添字を用いて要素にアクセスする。Queue: キュー。順序を持ち要素の重複も許す、FIFO (先入れ先出し) で要素にアクセスする。Deque: 両端キュー。順序を持ち要素の重複も許す、FIFO (先入れ先出し) に加えて LIFO (後入れ先出し) で要素にアクセスする。
Map: テーブル、連装配列とも。キーと値のペアの集合。
【バージョン】 JDK 1.1 まではコレクションがなく、それに代わるものとして相当するものとして、リストを表す
Vectorやテーブルを表すHashtableなどが存在していました。これらは現在コレクションとの互換性を持つように改修されていますが、古い API であるためできるだけ使用しないことを推奨します。
9.2.2. コレクションとジェネリクス (総称型)
コレクションは、どのクラスの要素を設定するものかを示すための「型パラメータ」と呼ばれるものを持ちます。コレクションに限らず、型パラメータを持つクラスのことを「ジェネリクス (総称型)」と呼びます。型パラメータは < T > のようにクラス名を < > で囲んで表現します。以下に例を挙げます。
List<String>:Stringクラスの要素を格納するListQueue<Integer>:Integerクラスの要素を格納するQueueMap<String, BigDecimal>:StringクラスのキーとBigDecimalクラスの値のペアからなるMap
型パラメータに指定できるのはクラスのみであるため、コレクションの要素にはプリミティブ型を設定することはできません。そこで、プリミティブ型に対応するラッパークラスを型パラメータに指定した上で (例: int 型の List の場合は List<Integer>)、プリミティブ型とラッパー型の相互変換を行うことになります。ただし、相互変換自体は自動で行われるため (オートボクシング機能)、通常はプリミティブ型のままでコレクションの操作を行って構いません。
9.2.3. コレクションのインスタンス生成式と変数宣言文
コレクションのインスタンス生成式は以下のようになります。
- 書式 (1):
new ClassName < T > ( [ args ] )ClassName: クラス名、T: 型パラメータ、args: コンストラクタ引数
- 書式 (2):
new ClassName <> ( [ args ] )ClassName: クラス名、args: コンストラクタ引数
- 書式 (1) はコレクションのインスタンス生成式の基本形です。
- 書式 (2) は、適用する型パラメータが明らかである場合の略式表記で、型パラメータを省略して代わりにダイヤモンド演算子
<>を記述します。確実に書式 (2) を適用できるケースは、インスタンス生成式を変数宣言文に記述する場合です (左辺式である変数のデータ型が型パラメータを明示するため)。
また、コレクションの変数宣言文は以下のようになります。
- 書式:
ClassName < T > var [ = expr ] ;ClassName: クラス名、T: 型パラメータ、expr: インスタンス生成式または既存のコレクション
- クラス名は、実際にはコレクションを表すインタフェース (
List、Set、Deque、Mapなど) を指定する場合がほとんどです。実装クラスを指定するケースは、独自のメソッドを使用したい場合に限られます (実際にはほとんどありません)。
コレクションのインスタンス生成式、変数宣言文、ダイヤモンド演算子の使用例を以下に示します。
// (1) コレクションのインスタンス生成式 (基本形)
// ここでは、List の実装である ArrayList のインスタンスを生成します
// 型パラメータは String とします
List<String> list1 = new ArrayList<String>();
// (2) コレクションのインスタンス生成式 (ダイヤモンド演算子)
// 左辺式のデータ型 List<String> より型パラメータが String であることは明らかのため、
// インスタンス生成式の型パラメータは省略できます。
List<String> list2 = new ArrayList<>();
ダイヤモンド演算子は Java SE 7 で導入された記法です。Java はダイヤモンド演算子から適切な型パラメータを導くために型推論という仕組みを用いていますが、Java SE 7 の型推論は限定的であり、そのためダイヤモンド演算子を使用できない場面が多くありました。Java SE 8 では 12 章で取り上げるラムダ式を導入する際に型推論の全面的な見直しが行われた結果、インスタンス生成式が使用できるほとんどの場所でダイヤモンド演算子が適用できるよう改善されています。
9.2.4. コレクションの実装と選び方
前節で述べた通り、コレクションにはそれぞれデータの保持方法が異なる複数の実装クラスがあります。保持方法によりアクセス特性に違いがあるため、最適な実装クラスを選択するようにしましょう。
| 保持方法 | Set<E> |
List<E> |
Deque<E> |
Map<K, V> |
|---|---|---|---|---|
| ハッシュ表 | HashSet |
N/A | N/A | HashMap |
| 可変配列 | N/A | ArrayList |
ArrayDeque |
N/A |
| B ツリー | TreeSet |
N/A | N/A | TreeMap |
| 連結リスト | N/A | LinkedList |
LinkedList |
N/A |
| ハッシュ表 + 連結リスト | LinkedHashSet |
N/A | N/A | LinkedHashMap |
| 保持方法 | 順次アクセス | 直接アクセス | 挿入・削除 |
|---|---|---|---|
| ハッシュ表 | 遅い | 非常に速い | 非常に速い |
| 可変配列 | 速い (サイズに依存) | 速い (サイズに依存) | 遅い |
| B ツリー | 普通 | 普通 | 速い (速度一定) |
| 連結リスト | 速い (サイズに依存) | 遅い | 速い (サイズに依存) |
コレクションの宣言と実装の選択は、以下の基本方針に従うと良いでしょう。
- 基本となるデータ型を決める:
String,Integer,BigDecimal, etc. - データ構造を決める:
Set<E>,List<E>,Deque<E>,Map<K, V>, etc. - データ構造の実装を決める
9.2.4.1. String クラス、順不同、重複なし
- 基本データ型 → String
- データ構造 = 順不同、重複なし → Set
- 実装 = 要件なし → HashSet
(推奨される実装) Set<String> set = new HashSet<>();
9.2.4.2. int 型、順序あり、直接アクセスあり
- 基本データ型 → Integer (ラッパー)
- データ構造 = 順序あり、直接アクセスあり → List
- 実装 = 要件なし → ArrayList
(推奨される実装) List<Integer> list = new ArrayList<>();
9.2.4.3. Integer クラス、順序あり、直接アクセスあり、挿入・削除が多い
- 基本データ型 → Integer
- データ構造 = 順序あり、直接アクセスあり → List
- 実装 = 挿入・削除多い → LinkedList
(推奨される実装) List<Integer> list = new LinkedList<>();
9.2.5. Collection の主なメソッド
Collection インタフェースにはいくつものメソッドが定義されていますが、その中からよく使われるものを以下に示します。これらはコレクション (Map を除く) で共通して使用できるメソッドであるため、押さえておきましょう。詳細は Java SE 標準 API のドキュメント を参照してください。
| メソッド名 | 説明 |
|---|---|
boolean add(E e) |
コレクションに要素を追加する |
boolean addAll(Collection <? extends E> c) |
コレクションにすべての要素を追加する |
void clear() |
コレクションの全要素を削除する |
boolean contains(Object o) |
要素がコレクションに含まれている場合は true、そうでなければ false を返す |
boolean isEmpty() |
コレクションの要素数が 0 の場合は true、そうでなければ false を返す |
Iterator<E> iterator() |
イテレータを返す (後述) |
boolean remove(Object o) |
コレクションの要素を削除する |
int size() |
コレクションの要素数を返す |
<T> T[] toArray(T[] a) |
コレクションを T 型/クラスの配列に変換する (後述) |
9.2.6. イテレータと拡張 for 文
コレクションは、要素にアクセスする標準的な手段として「イテレータ―」と呼ばれる仕組みを提供しています。イテレータとはコレクションの要素を指し示すもので、イテレータを介することによりコレクションの実装に依存しない形で要素にアクセスすることが可能となります。イテレータによるアクセスを順次アクセス (シーケンシャルアクセス) と呼びます。
コレクションのうち使用頻度の高い List では、後述のように配列同様に添字を用いてすべての要素にアクセスできるため、イテレータの必要性は感じられないかもしれません。しかし、他のコレクションである Set や Deque では添字を用いることができません。そのため、コレクションの種類によらず使用可能なイテレータが必要になってきます。
イテレータを持つクラスは、Iterable インタフェースを実装する必要があります。Collection インタフェースは Iterable インタフェースのサブインタフェースであるため、Collection から派生したコレクションはすべてイテレータをもっています。
イテレータは Iterator クラスのインスタンスであり、中心となるメソッドは以下の 3 種類です。
| メソッド | 説明 |
|---|---|
hasNext() |
次の要素がある場合は true、そうでなければ false (要素数が 0 の場合は常に false) |
next() |
次の要素を取得する |
remove() |
現在の要素 (= 前回 next() で取得した要素) を削除する |
以下にイテレータの使用例を示します。イテレータの使用時には、以下の点に注意してください。
- イテレータ取得時 (
Collection.iterator()呼び出し直後) はどの要素も指し示していない。 - 最初の要素を指し示すには
Iterator.next()メソッドを呼び出す。 - コレクションに要素がない場合は
Iterator.next()メソッドの呼び出しが失敗するため、事前にIterator.hasNext()メソッドでチェックする。 Iterator.remove()メソッドはあまり使われない。イテレータの状態を常に意識する必要があり、処理が煩雑になりがち。
Set におけるイテレータの使用例を以下に示します。
Set<String> set = new HashSet<>();
...
Iterator iter = set.iterator();
while (iter.hasNext()) {
String s = iterator.next();
System.out.println(s);
}
イテレータを利用した繰り返しを簡潔に表現するため、Java には拡張 for 文と呼ばれる特殊な繰り返しがあります。上記のイテレータの使用例を拡張 for 文で書き直したものを以下に示します。
(例) 拡張 for 文の使用例
Set<String> set = new HashSet<>();
...
for (String s : set) {
System.out.println(s);
}
拡張 for 文については 5 章でも取り上げましたが、この章で解説します。拡張 for 文の書式は以下の通りとなります。
- 書式:
for ( T var : expr ) stmtT: データ型、var: ローカル変数名、expr:Iterable実装のインスタンスまたは配列
exprがTクラスのイテレータを返すIterable実装か、またはT型の配列の場合に限って使用できます。exprを評価して取得した要素をvarに代入し、stmtを実行する処理を、exprのすべての要素に対して順番に実行します。- 拡張 for 文が、if-else 文
if ( expr ) stmt1 else stmt2のstmt1に該当する場合には、stmtには if 文以外を指定します。そうでない場合は任意の文を指定できます。
拡張 for 文は、以下のように while 文 (Iterable の実装クラス) または for 文 (配列) で書き換えることができます。
// expr が Iterable の実装の場合の書き換え (while 文)
Iterator<T> iter = expr.iterator();
while (iter.hasNext()) {
T var = iter.next();
stmt
}
// expr が配列の場合の書き換え (for 文)
for (int index = 0; index < expr.length; index++) {
T var = expr[index];
stmt
}
拡張 for 文には以下のようなメリットがあります。
- コレクション・配列を問わず同じ構文で扱うことができる。
- 要素の最後を判定する必要がない。
- 記述がシンプルになる。
一方で、拡張 for 文の使用には以下の条件があります。
- 繰り返し対象は
Iterableインタフェースの実装または配列。 - 繰り返し処理では要素の読み取りのみ可能。要素を追加・変更・削除することはできない。
拡張 for 文は必須の構文ではありませんが、コレクションを操作する上で非常に有益であるため、覚えておいた方が良いでしょう。
9.2.7. List、Queue、Deque の主なメソッド
Set には独自のメソッドは特に用意されていませんが、List、Queue および Deque には独自のメソッドが用意されています。List はリスト操作 (直接アクセス、ランダムアクセス)、Queue はキュー操作 (FIFO)、Deque はキュー操作 (FIFO) およびスタック操作 (LIFO) を提供します。
9.2.7.1. リスト操作 (list のみ)
List では、Collection の各メソッドに加え、以下のリスト操作メソッドが追加されています。詳細は Java SE 標準 API のドキュメント (java.util.List) を参照してください。
| メソッド名 | 説明 |
|---|---|
boolean add(int index, E element) |
index 番目に要素を挿入する |
E get(int index) |
index 番目の要素を取得する |
int indexOf(Object o) |
要素が最初に見つかった index を返す |
int lastIndexOf(Object o) |
要素が最後に見つかった index を返す |
int remove(int index) |
index 番目の要素を削除する |
E set(int index, E element) |
index 番目の要素を置き換える |
9.2.7.2. キュー操作 (Queue および Deque)
Queue および Deque では、Collection の各メソッドに加え、以下のキュー操作メソッドが追加されています。詳細は Java SE 標準 API のドキュメント (Queue) または Java SE 標準 API のドキュメント (java.util.Deque) を参照してください。
| メソッド名 | 説明 |
|---|---|
boolean add(E e) |
要素をキューへ挿入する |
boolean offer(E e) |
要素をキューへ挿入する (失敗した場合は非チェック例外 NoSuchElementException をスロー) |
E poll() |
要素を取得してキューから取り除く (要素がなければ null を返す) |
E element() |
要素を取得してキューから取り除く (要素がなければ非チェック例外 NoSuchElementException をスロー) |
E peek() |
要素を取得するがキューには残す (要素がなければ null を返す) |
E remove() |
要素を取得するがキューには残す (要素がなければ非チェック例外 NoSuchElementException をスロー) |
9.2.7.3. スタック操作 (Deque のみ)
Deque では、Collection の各メソッドに加え、以下のスタック操作メソッドが追加されています。詳細は Java SE 標準 API のドキュメント (java.util.Deque) を参照してください。
| メソッド名 | 説明 |
|---|---|
void push(E e) |
要素をスタックへ挿入する |
E pop() |
要素を取得してスタックから取り除く |
E peek() |
要素を取得するがスタックには残す |
9.2.8. Map の主なメソッド
Map では以下のスタック操作メソッドが用意されています。Map は Collection のサブインタフェースではないためメソッドに共通性がないことに注意してください。詳細は Java SE 標準 API のドキュメント (java.util.Map) を参照してください。
| メソッド名 | 説明 |
|---|---|
void clear() |
すべてのエントリ (キーと値のペア) を削除する |
boolean containsKey(Object key) |
キーが含まれている場合は true、そうでない場合は false を返す |
Set<K> keySet() |
すべてのキーを返す (Set インタフェース) |
V get(Object key) |
キーに対応する値を取得する (存在しない場合は null を返す) |
V getOrDefault(Object key, V defaultValue) |
キーに対応する値を取得する (存在しない場合は defaultValue を返す) |
V put(K key, V value) |
キーに対応する値を設定する (既に存在する場合は置き換える) |
V putIfAbsent(K key, V value) |
キーに対応する値を設定する (既に存在する場合は置き換えない) |
V remove(Object key) |
キーと対応する値を削除する (存在しない場合は何もしない) |
int size() |
エントリ (キーと値のペア) の数を返す |
Collection<V> values() |
すべての値を返す (Collection インタフェース) |
getOrDefault() および putIfAbsent() メソッドは Java SE 8 で追加されたものです。get() および put() メソッドと比較して、不要な null チェックを回避できる (すなわち NullPointerException がスローされにくい) 点で優れています。この他にも Map には Java SE 8 で追加されたメソッドが多数あります。
9.3. Arrays クラスと Collections クラス
9.3.1. Arrays クラス
Arrays クラスは配列の操作に便利なユーティリティ・クラスです。配列の操作は繰り返し構文を用いて実装することも可能ですが、Java SE 標準 API として用意され、テストも十分に行われている Arrays クラスのメソッドを利用することを強く推奨します。Arrays クラスの詳細は Java SE 標準 API ドキュメント (java.util.Arrays) を参照してください。
| メソッド名 | 説明 |
|---|---|
asList |
指定された配列に連動する固定サイズのリストを返す |
binarySearch |
配列から指定された値を検索する (二分検索) |
copyOf |
指定された配列をコピーする (長さは必要に応じて切り詰めるかパディング) |
copyOfRange |
指定された配列の指定された範囲を新しい配列にコピーする |
equals |
2 つの配列が互いに同等である場合に true を返す |
hashCode |
指定された配列の内容に基づくハッシュ・コードを返す |
toString |
指定された配列の文字列表現を返す |
deepEquals |
2つの配列が互いに等価な場合に true を返す (深層内容) |
deepHashCode |
指定された配列のハッシュ・コードを返す (深層内容) |
deepToString |
指定された配列の文字列表現を返す (深層内容) |
fill |
配列の各要素を設定する (同一の値を使用する) |
sort |
指定された配列をソートする |
setAll |
配列のすべての要素を設定する (異なる値も設定可) |
parallelSort |
指定された配列をソートする (マージソード・並列処理) |
parallelSetAll |
配列のすべての要素を設定する (異なる値も設定可・並列処理) |
equals、hashCode、toString はプリミティブ型およびクラスの配列に使用可能で、単純な比較、ハッシュ・コード計算、文字列表現取得を行います。deepEquals、deepHashCode、deepToString はクラスの配列に使用可能で、インスタンスの詳細まで参照して比較、ハッシュ・コード計算、文字列表現取得を行います。一見すると deep ~の方が優れているようにも見えますが、どのレベルまで参照すべきかはユースケースにより異なりますので、一概に優劣はつけられません。
fill は配列の各要素を同じ値で埋めるために使用します。setAll、parallelSetAll も配列の各要素を埋める点では同じですが、添字によって異なる値を設定可能である点が異なります。
sort、setAll は逐次処理で実行されますが、parallelSort、parallelSetAll は並列処理で実行されます。扱う要素数が多く CPU パワーにも余裕がある場合は並列処理を、扱う要素数が少ないか CPU パワーに余裕がない場合は逐次処理を選択するのが効率的です。通常使用する範囲では逐次処理で問題はありません。
setAll,parallelSetAll,parallelSortは Java SE 8 で追加されたメソッドで、12 章で取り上げるラムダ式を活用しています。
9.3.2. Collections クラス
Collections クラスはコレクションの操作に便利なユーティリティ・クラスです。コレクションをデータ構造とするならば、Collecions クラスは汎用的なアルゴリズムに相当するものです。コレクションの操作は繰り返し構文を用いて実装することも可能ですが、Java SE 標準 API として用意され、テストも十分に行われている Collections クラスのメソッドを利用することを強く推奨します。Collections クラスの詳細は Java SE 標準 API ドキュメント (java.util.Collections) を参照してください。
| メソッド名 | 対象 | 説明 |
|---|---|---|
addAll |
Collection |
指定されたすべての要素を Collection に追加する (高速) |
binarySearch |
List |
リストからインスタンスを検索する (二分探索) |
copy |
List |
あるリストから別のリストにすべての要素をコピーする |
disjoint |
Collection |
2 つの Collection に共通の要素がない場合に true を返す |
emptyIterator |
N/A | 空のイテレータを返す |
emptyList |
N/A | 空の List を返す |
emptyMap |
N/A | 空の Map を返す |
emptySet |
N/A | 空の Set を返す |
fill |
List |
すべての要素を指定した要素で置き換える |
frequency |
Collection |
指定されたオブジェクトと等価な要素の数を返す |
indexOfSubList |
List |
サブ・リストが最初に出現した位置の開始位置を返す |
lastIndexOfSubList |
List |
サブ・リストが最後に出現した位置の開始位置を返す |
max |
Collection |
最大の要素を返す |
min |
Collection |
最小の要素を返す |
nCopies |
N/A | 指定されたインスタンスの n 個のコピーで構成される不変の List を返す |
replaceAll |
List |
指定された値をすべて置き換える |
reverse |
List |
リストの要素の順序を逆にする |
rotate |
List |
リストの要素を指定された距離により回転する |
shuffle |
List |
リストの順序をシャッフルする |
singleton |
N/A | 指定されたインスタンスだけを格納する不変の Set を返す |
singletonList |
N/A | 指定されたインスタンスだけを格納する不変の List を返す |
singletonMap |
N/A | 指定されたインスタンスだけを格納する不変の Map を返す |
sort |
N/A | リストを昇順ソートする |
swap |
List |
リストの指定された位置にある要素を入れ替える |
synchronizedCollection |
Collection |
同期化 (10 章) された Collection を返す |
synchronizedList |
List |
同期化 (10 章) された List を返す |
synchronizedMap |
Map |
同期化 (10 章) された Map を返す |
synchronizedSet |
Set |
同期化 (10 章) された Set を返す |
unmodifiableCollection |
Collection |
不変な Collection を返す |
unmodifiableList |
List |
不変な List を返す |
unmodifiableMap |
Map |
不変な Map を返す |
unmodifiableSet |
Set |
不変な Set を返す |
emptyIterator は Java SE 7 で追加されたメソッドです。また、補助的な役割のため上表からは割愛しましたが、Java SE 8 で追加されたメソッドがいくつか存在しています。
9.4. 配列とコレクションの相互変換
この節では、実際のアプリケーションで多用するであろう、配列とコレクションの相互変換について取り上げます。Java はデータ型の判断に厳しい言語であるため少し冗長な記述となりますが、実際には定型処理となるため一度やり方を覚えてしまえば後々まで活用することができます。
9.4.1. 配列からコレクションへの変換
配列は Arrays クラスの asList メソッドを用いて List に変換可能です。その他のコレクション、例えば Set や Deque への変換は、一旦 List に変換してから行います。
// 変換前の配列
String[] strings = new Strings[] { "Able", "Baker", "Charlie" };
// 配列から List への変換
List<String> stringList = Arrays.asList(strings);
// 配列から Set への変換 (List 経由)
Set<String> stringSet = new HashSet<>(Arrays.asList(strings));
// 配列から Deque への変換 (List 経由)
Deque<String> stringDeque = new ArrayDeque<>(Arrays.asList(strings));
Arrays クラスの asList メソッドは可変長引数となっており、配列だけでなく、任意個のインスタンスからなる List を手軽に作成可能です。例えば、以下のようになります。
// Arrays.asList だけで List を作成可能
List<String> stringList = Arrays.asList("Able", "Baker", "Charlie");
9.4.2. コレクションから配列への変換
Collection インタフェースに toArray メソッドが用意されています。
Object[] toArray()T[] toArray(T[] array)
最初の書式は引数なしで呼び出せますが、変換後の配列は Object クラスの配列になります。Object クラスの配列は各要素に任意のクラスのインスタンスを設定可能であるため、元のクラスの配列にキャストするのは非常に手間がかかります (一括でキャストできる場合もありますが安全ではありません)。
2 番目の書式は引数に出力先の配列を指定して、その配列に要素を格納するものです。引数の配列の要素数はコレクションと一致していなくても構いません (配列とコレクションの要素数が一致している場合には、配列がそのまま出力となるため高速に処理されます)。こちらの書式は配列生成式を設定する手間がかかるものの、本来のデータ型の配列に変換できるため、結果的には有利な方法になります。
List<String> list = new ArrayList<>();
list.add("Able");
list.add("Baker");
list.add("Charlie");
// List から配列への変換 (1)
// 引数の配列は要素数が少なくても (空の配列であっても) OK
String[] array1 = list.toArray(new String[0]);
// List から配列への変換 (2)
// 引数の配列がコレクションと同じ要素数の場合、配列の調整が行われないため高速
String[] array2 = list.toArray(new String[list.size()]);
9.4.3. Map から配列への変換
Map を配列に直接変換する方法は用意されていません。Map がキーと値のペアによる 2 次元のデータ型であるのに対して、配列は連続する要素型で構成される 1 次元のデータ型であるためです。例えば、配列の要素型にキーと値のペアを格納するクラスを指定し、Map の要素をそれに割り当てるようなアプローチを採る必要が出てきます。
9.5. 配列・コレクションと Stream API の変換について
配列・コレクションと 12 章で取り上げる Stream API も相互変換が可能です。典型的な使い方としては、まずに処理するデータを配列またはコレクションで用意し、Stream API に変換した後にデータの加工を行い、結果を配列またはコレクションに戻す方法が挙げられます。Stream API でデータを処理するのは、Stream API 自体が強力なデータ処理機能を有しており (Java SE 8 で導入されたため当然と言えます)、Arrays や Collections を駆使するよりも容易にデータの加工が可能となるためです。また、Stream API は並列処理に対応しているため、要素数が多い場合には Stream API に変換して並列処理を行った方がパフォーマンス上有利である点も上げられます。