本章では、複数のオブジェクトをまとめて保持するコレクションに関して学習します。
本章で学習する内容を動画としてまとめたものです。最初に一通り見終わった後で、学習に入るようにしてください。
プログラムでは、複数のデータをまとめて扱いたいことがあります。これまでに「配列」は学習しましたが、これから学習する「コレクション」も同様に複数のデータを保持するための機能です。
Javaでは、「配列」よりも「コレクション」を使う機会が多いです。
「配列」は一度生成すると保持できる要素の数が決まってしまいます。処理によっては、保持したい要素の数が未確定のこともあり、このようなときには、「コレクション」を使うと便利です。
コレクションは、配列と異なり、要素の数が可変です。そのため、入れたいデータを入れたいだけ追加することができます。
ただし、コレクションに保持できるデータは、オブジェクトのみです。基本データ型であるint型の数値(例えば、10)をそのままコレクションに格納できません。
int型の数値をStringやIntegerのオブジェクトに変換してからコレクションに追加する必要がありますので、注意しましょう。
なお、JDK5からは、後述するオートボクシング機能が追加されたため、オブジェクト変換を自動で行ってくれるようになっています。
コレクションには6つの主要インターフェースがあり、これらを「コアインターフェース」と呼びます。
コレクションに関連するAPIは、「java.util」というパッケージに属していますので、使用する場合には、import文が必要となります。
以下がコレクションのコアインターフェースです。
インターフェース | 概要 |
---|---|
Collection |
コレクションを表現する最も基本的なインターフェースです。 要素の登録、削除、要素数や存在の問い合わせ、イテレータの生成を行うメソッドが基本となります。 |
List |
要素が順序づけられたコレクションのインターフェースです。 インデックスを使用して要素にアクセスすることが可能となります。 |
Set |
重複を許さないコレクションのインターフェースです。 重複を許容しない以外はListに似ています。 |
SortedSet |
Setインターフェースを継承し、要素の順番を内部で保持するコレクションのインターフェースです。 要素の順番はキーの大小関係で決まります。 Comparable/Comparatorインターフェースによってキーの大小関係が規定されます。 |
Map |
「キー(key)」と「値(value)」を1組としてデータを保持するインターフェースです。 キーの指定による値の取り出しや削除などをすばやく行うことができます。 |
SortedMap |
Mapインターフェースを継承し、要素の順番を内部で保持するコレクションのインターフェースです。 要素の順番はキーの大小関係で決まります。 Comparable/Comparatorインターフェースによってキーの大小関係が規定されます。 |
その他、コレクションを便利に利用するためのクラスが用意されています。
クラス | 概要 |
---|---|
Collections | コレクションを返す各種メソッド群 |
Arrays | ソートや検索等の便利なメソッド群 |
コレクションの概要説明は以上です。それでは、代表的なコレクションの機能を一つ一つ学習していきましょう。
コレクションは、インターフェースを中心として仕様が作成されていますが、インターフェースなので、それをimplementsしたクラスも存在します。
実際には、これらのクラスを利用していくことになります。
Listインターフェースを実装した代表的なクラスは、ArrayListです。
Listインターフェースを実装したクラスは、個数があらかじめ予測できないデータの集まりを取り扱うのに便利です。
例えば、データベースからデータを取得してそのデータを保持する場合、ショッピングサイトのお買い物カゴのようにユーザの入力に応じて保持するデータを追加する場合などに利用されます。
Listインターフェースは、追加、挿入、削除、検索、ランダムアクセスのメソッドを定義しています。
以下が主要なメソッドの一覧です。
戻り値 | メソッド名 | 概要 |
---|---|---|
boolean | add(Object) | リストの最後にオブジェクトを追加します |
boolean | add(int, Object) | 第1引数で指定されたインデックスにオブジェクトを追加します ※ インデックスは0から始まります |
boolean | contains(Object) | 引数で指定されたオブジェクトが含まれている場合に「true」を返します |
boolean | isEmpty() | リストに要素がない場合に「true」を返します |
Object | get(int) | 引数で指定されたインデックスにあるオブジェクトを返します |
Object | remove(int) | 引数で指定されたインデックスにあるオブジェクトを削除します |
Object | set(int, Object) | 第1引数で指定されたインデックスにあるオブジェクトを、第2引数のもので置換します |
int | size() | リストの要素数を返します |
ArrayListクラスはこれらのメソッドを実装しています。
ArrayListクラスの使用説明の前に、JDK5から追加された文法の「Generics(総称)」について説明を行います。
Generics(ジェネリクス)はJDK5から追加された文法です。
コレクションクラスには基本データ型を格納できませんが、逆に言えば、オブジェクトであれば何でも格納できてしまいます。コレクションクラスは追加した要素の出し入れは、全てObject型として扱います。
そのため、コレクションクラスから値を取得する場合、値は全てObject型として取得されてしまいます。
Object型の状態ではString型の変数等に値の格納がコンパイルエラーにより行えませんので、取得した値をString等の変数に格納する場合、キャストを行う必要があります。
以下はJDK5より前のバージョンでの記述方法です(以下のコードでも動作させることは可能です)。
List list = new ArrayList();
String str = "あ";
list.add(str);
// String型でListに格納されている値がObject型で取得されてしまうためString型にキャスト(変換)してString型の変数に代入する
String str2 = (String) list.get(0);
このコードは正常に動作しますが、例えば以下のコードのようにプログラミングミスが原因で、キャストを行う型を誤ってしまった場合は正常に動作しません。
List list = new ArrayList();
String str = "あ";
list.add(str);
// String型でListに格納されている値をInteger型にキャスト(変換)するため例外が発生
Integer i = (Integer) list.get(0);
この場合、String型で格納されている値をInteger型でキャストを行っているので、java.lang.ClassCastException(例外)が発生し、処理が中断されてしまいます。
Genericsはこういったプログラミングミスを減少させるため誤った値の格納をコンパイルエラーで知らせてくれる役割を果たしてくれます。
基本構文は以下になります。これまでとは少し異なる書き方なので注意して下さい。
List<格納する型> list = new ArrayList<格納する型>();
この内容ではList型の変数listには「格納する型」の値のみを格納するということをコンパイラに宣言しています。
例としてString型の値のみを格納するListを宣言する場合は以下になります。
List<String> list = new ArrayList<String>();
そのため下記コードはGenericsによる宣言を行っているためコンパイルエラーとなり、実行が行えません。
List<String> list = new ArrayList<String>();
Integer i = Integer.valueOf(100);
// String型のみ格納できる宣言を行っているListに対してInteger型の変数を格納しようとしたためコンパイルエラー
list.add(i);
このようにGenericsはコレクションに格納する型を確定させることが可能です。
Java7より「格納する型」を省略する書き方も可能になりました。
「<>」部分を「ダイヤモンド演算子」と呼びます。
インスタンスを生成し、変数へ代入する際に利用可能です。
下記のように、new演算子(右側)を用いている部分のみ「格納する型」が省略可能であると覚えておきましょう。
List<String> list = new ArrayList<>();
以下は、ArrayListクラスに要素を追加して、追加したデータを出力するサンプルです。
import java.util.ArrayList;
import java.util.List;
public class ListSample {
public static void main(String[] args) {
// ArrayListクラスのインスタンス化
List<String> list = new ArrayList<String>();
// リストに項目を追加します
list.add("あ");
list.add("い");
list.add("う");
// 追加したデータの取得には 拡張for文を使用します
for (String str : list) {
// 要素の取得
System.out.println(str);
}
}
}
ArrayListクラスを使用する場合、まず最初にインスタンス化します。通常、インスタンス化したオブジェクトの変数の型は、Listインターフェースで定義します。
// ArrayListクラスのインスタンス化
List<String> list = new ArrayList<String>();
また、要素の追加には、addメソッドを使用します。
// リストに項目を追加します
list.add("あ");
list.add("い");
list.add("う");
追加したデータの取得は、拡張for文を使用しています。
// 追加したデータの取得には 拡張for文を使用します
for (String str : list) {
// 要素の取得
System.out.println(str);
}
コレクションに格納できるデータはオブジェクトのみであるため、intなどの基本型をコレクションに格納する場合、以下のサンプルソースのようにラッパークラスを使用する必要があり、非常に面倒でした。
pList<Integer> list = new ArrayList<Integer>();
int i = 5;
list.add(Integer.valueOf(3)); // int型をInteger型に変数してリストに格納
list.add(Integer.valueOf(i)); // int型をInteger型に変数してリストに格納
しかし、JDK5からはオートボクシング機能という新機能が追加されたことにより、面倒な処理が不要となりました。
この機能は、コレクションクラスに基本データ型を格納する際、自動的に対応するラッパークラスに型変換をしてくれるというものです。
オートボクシング機能を使用することにより以下のように記述することができるようになります。
List<Integer> list = new ArrayList<Integer>();
int i = 5;
list.add(3); // 整数値をリストに格納
list.add(i); // int型の変数をリストに格納
また、コレクションから値を取り出して基本型の変数に格納する際も自動的に型変換してくれます。
この機能をオートアンボクシングといいます。
以下はオートアンボクシングを使用してコレクションから取り出した値を基本型に格納するサンプルです。
List<Integer> list = new ArrayList<Integer>();
list.add(3); // 整数値をリストに格納
int i = list.get(0); // リストの先頭要素をint型の変数に格納
注意点としては、オートボクシング機能によりあたかも基本データ型をコレクションに格納するようなコードを書くことができるようになりましたが、コレクションクラスに格納できるデータはあくまでオブジェクトのみ、ということです。
ラッパークラスが基本データ型に取って代わられたわけではありません。常に内部では型変換が行われているというところに注意する必要があります。
Listに関する説明は以上となります。
java.util.Mapは、キーとデータを関連付けて保存するコレクションです。Mapのキーに対して保持できる値は1つです。
Mapインターフェースを実装したクラスは、キーとなる値から対応する値を取得したい場合に利用します。
以下はjava.util.Mapの主要なメソッド一覧です。
戻り値 | メソッド名 | 概要 |
---|---|---|
Object | get(Object) | 引数で指定されたキーに関連付けられたデータを返します |
Object | put(Object, Object) | 第1引数を引数をキーとして、第2引数のデータを関連付けし、すでに値がある場合、置き換えます |
Object | remove(Object) | 引数で指定されたキーに対して関連付けられたデータがある場合、それを削除します。戻り値は削除されたデータです |
void | clear() | 保存されている値をキーも含めて削除します |
Set | keySet() | 保存されているすべてのキーをSetオブジェクトで返します |
Collection | values() | 保存されているすべての値をCollectionオブジェクトで返します |
Set | entrySet() | 保存されているすべてのキーと値のペアをSetオブジェクトで返します |
boolean | containsKey(Object) | 引数で指定されたオブジェクトがキーとして存在する場合にtrueを返します |
boolean | containsValue(Object) | 引数で指定されたオブジェクトがデータとして保存されている場合にtrueを返します |
int | size() | 保存されているデータの個数を返します |
Mapインターフェースを実装するクラスで、必ずしもすべてのメソッドが使えるとは限りません。使えない場合には、
java.lang.UnsupportedOperationException例外が発生しますので、注意しましょう。
Mapを実装したクラスで最も利用するのがHashMapです。
Mapの場合、キーから値を検索して取得する処理が一番多く使われます。
キーと値の追加は、putメソッド、値の検索は、getメソッドを利用します。
以下はputメソッドでキーと値を追加し、getメソッドを利用してキーに一致する値を取得しているサンプルです。
import java.util.HashMap;
import java.util.Map;
public class MapSample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("大阪府", "大阪市");
map.put("兵庫県", "神戸市");
map.put("滋賀県", "大津市");
// 兵庫県をキーとした場合の値を取得する
String val = map.get("兵庫県");
System.out.println("val = " + val);
// キーに存在しない場合は null が取得される
val = map.get("東京都");
System.out.println("val = " + val);
}
}
まず、HashMapクラスのオブジェクトの生成は以下のとおりです。Listのときと同様で、型はMapインターフェースで定義するのが通常です。
// HashMapクラスのインスタンス化
Map<String, String> map = new HashMap<String, String>();
ここでもHashMapに対してGenericsによる型指定を行っています。Listの時と異なり、指定している型が複数あることに注意して下さい。
Listの場合は格納する値が1つだけでしたので、Genericsによる格納する値の型を指定する記述は1つでした。しかし、Mapは値をキーと値の2つ格納するため、指定する型も2つ存在します。
以下がHashMapクラスのオブジェクトにデータを追加する記述です。マップは、キーと値の2つのデータをセットで追加します。
// mapにキーと値のセットでデータ追加
map.put("大阪府", "大阪市");
map.put("兵庫県", "神戸市");
map.put("滋賀県", "大津市");
以下の記述がキーから値を取得している部分です。getメソッドの引数に検索のキーとなる値をセットすると、それに見合う値が取得できます。
// 兵庫県をキーとした場合の値を取得する
String val = map.get("兵庫県");
System.out.println("val = " + val);
Mapに格納されたキーを全件取得したい場合は、keySetメソッド、値を全件取得したい場合は、valuesメソッドを利用します。
以下がkeySetメソッド、valuesメソッドを使ったサンプルです。
import java.util.HashMap;
import java.util.Map;
public class MapSample2 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("大阪府", "大阪市");
map.put("兵庫県", "神戸市");
map.put("滋賀県", "大津市");
// keySetメソッドを利用してキーを全件取得する
for (String key : map.keySet()) {
System.out.println(key);
}
// valuesメソッドを利用して値を全件取得する
for (String value : map.values()) {
System.out.println(value);
}
}
}
また、キーと値の両方が必要な場合は、entrySetメソッドを利用します。
以下がentrySetメソッドを使ったサンプルです。
import java.util.HashMap;
import java.util.Map;
public class MapSample3 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("大阪府", "大阪市");
map.put("兵庫県", "神戸市");
map.put("滋賀県", "大津市");
// entrySetメソッドを利用してキーと値を全件取得する
for (Map.Entry<String, String> entry : map.entrySet() ) {
// Map.EntryインタフェースのgetKeyメソッドでキーを取得する
String key = entry.getKey();
System.out.println(key);
// Map.EntryインタフェースのgetValueメソッドで値を取得する
String value = entry.getValue();
System.out.println(value);
}
}
}
HashMapクラスの説明は以上となります。
java.util.HashMapクラスの他にもMapインターフェースを実装したクラスが存在します。
以下がそれらのクラスです。
クラス名 | 特徴 |
---|---|
HashMap | 最もよく利用するMap。通常はこれを使います |
Hashtable | 古いMap。同期するのが特徴です |
TreeMap | キーが辞書順にソート(並び替え)されています |
LinkedHashMap | キーが追加した順番に並んでいます。 |
IdentityHashMap | 通常のMapはequalsによりキーの同一を確認しますが、これは==を利用します |
Mapに関する説明は以上です。
java.util.Setは、List同様複数の値をインデックスなどと関連付けずに記憶するコレクションクラスです。
Listとの違いは重複して同じオブジェクトを保持できない点です。
hashCodeメソッドの値が同じで、かつequalsメソッドがtrueである場合にオブジェクトは同一と判断され、後から追加した値で上書きされます。
Setインターフェースを実装するクラスには、java.util.HashSet、java.util.TreeSet、java.util.LinkedHashSetがあります。
以下がSetの主要なメソッドの一覧です。
戻り値 | メソッド名 | 概要 |
---|---|---|
boolean | add(Object) | 引数で指定されたオブジェクトがセットに含まれない場合、セットに追加し、追加された場合trueを返します |
boolean | contains(Object) | 引数で指定されたオブジェクトが含まれている場合にtrueを返します |
void | clear() | すべての要素を削除します |
boolean | remove(Object) | 引数で指定したオブジェクトを削除し、指定したオブジェクトがセット内にある場合trueを返します |
int | size() | 要素数を返します |
Object[] | toArray(Object[]) | セットを引数で指定した型の配列に変換して返します |
Setを実装している代表的なクラスは、HashSetクラスです。
以下がHashSetクラスを使ったサンプルです。
import java.util.HashSet;
import java.util.Set;
public class SetSample {
public static void main(String[] args) {
// HashSetクラスのインスタンス化
// 左側の型が Set 型である事に注目してください
Set<String> set = new HashSet<String>();
// setに項目を追加します
set.add("あ");
set.add("い");
set.add("う");
// 追加したデータの取得には 拡張for文 を使用します
for (String str : set) {
// 要素の取得
System.out.println(str);
}
}
}
メソッド一覧からわかるとおり、Setには特定の要素を取り出すメソッドがありません。要素の取得には拡張for文を使用して順番に取得する必要があります。
HashSetを利用した場合どのような順番で出力されるかはわかりません。
java.util.HashSetクラスの他にもSetインターフェースを実装したクラスが存在します。
以下がそれらのクラスです。
クラス名 | 特徴 |
---|---|
HashSet | 最もよく利用するSet。通常はこれを使います |
TreeSet | 値が辞書順にソート(並び替え)されています |
LinkedHashSet | 値が追加した順番に並んでいます。 |
Setの説明は以上となります。
コレクションの練習問題です。
「りんご」、「バナナ」、「なし」、「もも」をListに追加し、拡張for文を使用して、コンソールに表示してください。
import java.util.ArrayList;
import java.util.List;
public class ListMondai1_1 {
public static void main(String[] args) {
// リストの生成
List<String> list = new ArrayList<String>();
// リストに要素を追加
list.add("りんご");
list.add("バナナ");
list.add("なし");
list.add("もも");
// 格納したリストの要素を出力
for (String str : list) {
System.out.println(str);
}
}
}
「入出力ストリーム」の章のサンプルソースを参考に、コンソールに以下のように「りんご」、「バナナ」、「なし」、「もも」を順番に入力できるプログラムを作成してください。
「!」 + Enterで終了
この後に入力 >
りんご
この後に入力 >
バナナ
この後に入力 >
なし
この後に入力 >
もも
また、入力した値をListに格納し、そのあとに拡張for文を使用して、コンソールに表示してください。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public class ListMondai1_2 {
public static void main(String[] args) {
// メッセージ出力
System.out.println("「!」 + Enterで終了");
// 入力された文字列格納用リスト生成
List<String> list = new ArrayList<String>();
BufferedReader br = null;
try {
// コンソールの入力データ読み込み準備
br = new BufferedReader(new InputStreamReader(System.in));
// 読み込み格納用変数定義
String str = "";
// 「!」が入力されるまで読み込み繰り返し
while (!str.equals("!")) {
// メッセージ出力
System.out.println("この後に入力 >");
// コンソールの入力データ読み込み
str = br.readLine();
//「!」以外の入力した値をリストに格納
if (!str.equals("!")) {
list.add(str);
}
}
// リストの各要素を出力
for (String st : list) {
System.out.println(st);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
// 読み込みストリームをクローズします
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
コンソールに入力した値を読み込む処理については、「入出力ストリーム」の章のサンプル通りに記述します。
読み込んだ文字列をリストに順次格納していきますが、読み込みの繰り返し判定に使用している「!」をリストに 追加しないように、if文で判定処理を入れるようにしましょう。
最後に、格納した各要素を拡張for文で取得し、コンソールに出力します。
C:\temp\mondailist.txtを作成し、以下の内容を入力して保存してください。
りんご
バナナ
なし
マスカット
メロン
バナナ
そして、以下の仕様を満たすプログラム「ListMondai1_3」を完成させてください。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class ListMondai1_3 {
public static void main(String[] args) {
BufferedReader br = null;
try {
// ファイルの読み込み準備
br = new BufferedReader(new FileReader("C:\\temp\\mondailist.txt"));
// 読み込み格納用変数定義
String str = "";
List<String> list = new ArrayList<String>();
// 読み込み処理
while ((str = br.readLine()) != null) {
/* 1. ここに「ファイルを読み込んだ値をListに格納する処理」を追記 */
}
/* 2. リストの内容をコンソールに表示する処理を追記 */
System.out.println("=====================================");
/* 3. リストの内容から重複データを除外する処理を追記 */
/* 4. 重複除外後のデータをコンソールに表示する処理を追記 */
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
// 読み込みストリームをクローズします
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
※この問題の解答は掲載しておりません。Tech Fun ITスクールのJava研修では、講師が丁寧に解説しています。
キーが「りんご」で値が「apple」、「バナナ」で「banana」、「なし」で「pear」、「もも」で「peach」をMapに追加し、コンソールに日本語名を入力して、その日本語名に合った英語名を表示するプログラムを作成してください。
以下が実行イメージです。
Mapに入力された日本語名の果物が格納されていない場合、以下のように「わかりません。」を表示してください。
「!」 + Enterで終了
この後に入力 >
りんご
りんごを英語で表現すると・・appleです。
この後に入力 >
もも
ももを英語で表現すると・・peachです。
この後に入力 >
すいか
すいかを英語で表現すると・・わかりません。
※この問題の解答は掲載しておりません。Tech Fun ITスクールのJava研修では、講師が丁寧に解説しています。
コレクションは実務で非常によく使われていますので、理解があやふやだと感じた場合は講師に質問するなどして疑問を解消しておきましょう。
コレクションについての説明は、以上です。