ArrayListについて

今回は JavaArrayList に焦点を当ててみたいと思います。
ArrayList は配列のようなものですが、配列とは異なり、初期化時に長さを指定しなくても良い、後から自由に長さを変更できる便利なクラスです。
 
まず、配列の例を見てみましょう。

String array1[] = new String[2]; // 要素数2の配列を宣言
array1[0] = "りんご";
array1[1] = "いちご";
array1[2] = "みかん"; // 3個目の要素は設定できない(例外が発生する)

 
そして、ArrayList の例を見てみましょう。

List array2 = new ArrayList(); // 宣言時に要素数の指定は不要
array2.add("りんご");
array2.add("いちご");
array2.add("みかん"); // 好きなだけ追加可能

あらかじめ扱う要素数が決まっている場合は配列でも十分ですが、要素数が不明確な場合、ArrayList は非常に便利です。
実際の実務では、多くの場面で「要素数が不明確な場合」がほとんどです。
 
ただし、ArrayList をそのまま使用すると、いくつかの不便な点があります。
例えば、以下のコードはコンパイルは正常に終了しますが、実行時に
 「(String) array1.get(0)」
の箇所で例外が発生します。

List array3 = new ArrayList();
array3.add(1);  // 文字列ではなく、数値型を設定してしまった
String strItem = (String)array3.get(0);

 
実行時のエラーメッセージは次の通りです。

java.lang.ClassCastException:java.lang.Integer cannot be cast to java.lang.String

サンプルコードの記述ではArrayListの引数はObject型となっています。
そのため、どんな型でもaddできてしまいます。
今回の例では、文字列型の「"1"」をaddするつもりで、誤って数値型の「1」をaddしてしまいました。
しかし、引数の型はObject型なので、エラーにはなりません。
その後、getする際に文字列型に型変換を行っているため、実行時例外となってしまいました。
 
この不具合の原因の特定は意外と大変です。
get の部分で例外が発生していますが、真の原因は add している箇所にあります。
今回のサンプルはたった4行しかありませんが、コードが複雑になるにつれ、add する部分と get する部分は離れていき、原因特定が難しくなります。
 
そこで、「ジェネリクス(Generics)」の登場です。
ジェネリクスArrayList 専用の機能ではなく、もっと汎用的な概念ですが、今回は ArrayList の使い方を改善する方法に焦点を当てて説明します。

List<型> 変数名 = new ArrayList<型>();

List<型> 変数名 = new ArrayList<>(); // java 7以降は省略が可能

ジェネリクスを使用した ArrayList の宣言方法はこのようになります。
クラスの後に"<"と">"で囲み、型を指定できます。
この型指定部分は「型パラメータ」と呼ばれます。
型パラメータを指定することで、ArrayList で扱う要素の型を特定できます。
 
これによってどう変わるのか、先ほどの例で説明します。

List<String> array4 = new ArrayList<>();
array4.add(1);  // String 型の引数のみを受け付ける

上記の例では array4 は「String を扱う ArrayList」として宣言され、その結果、 array4.add(1) はビルド時にエラーとなります。
 
エラーメッセージ

型 ArrayList<String> のメソッド add(int, String) は引数 (int) に適用できません

これにより、コーディングミスを早期に発見できます。
また、型パラメータを指定しない場合、get メソッドの戻り値は Object 型となり、キャストが必要です。
しかし、型パラメータを指定すると、戻り値も指定した型となり、型変換が不要になります。(地味に便利です)
 
型パラメータ指定あり

List<String> array5 = new ArrayList<>();
array5.add("1");
String strGenItem = array5.get(0); // getの戻り値はString型になる

型パラメータ指定なし

List array6 = new ArrayList();
array6.add("1");
// getの戻り値はObject型なのでStringにキャストが必要
String strItem = (String)array6.get(0);

型パラメータを指定するということはコードの簡潔さと可読性を向上させます。
ArrayList を含むコレクションクラスを使用する際には、型パラメータを活用してコードの安全性、利便性、保守性を向上させましょう。