Javaの「Listクラス」は、要素を自由に追加・削除できる「動的配列」です。通常の配列は、一度作成したら要素数を変更できません。Listクラスを活用すれば、プログラムの状況に応じてサイズを変更できるだけではなく、さまざまな機能も利用できるので便利です。
ただし、JavaのListクラスはメソッドが非常に多く、使いこなすためには各メソッドの使い方を理解しておくことが重要です。本記事では、JavaのListクラスの基本的な使い方に加えて、さまざまなメソッドの使い方と応用テクニックをサンプルコード付きで解説します。
目次
Javaの「Listクラス」は、同じデータ型の変数をまとめて管理できるクラス。Javaの通常の配列もこの点では同じです。ただし、Listクラスは通常の配列とは異なり、生成後も要素数の追加と削除が自由に行えることがポイントになります。
通常の配列は、生成時に要素数を指定する必要があり、一度決めた要素数は変更できません。そのため、プログラムが進行して「要素を追加したい」あるいは「削除したい」と思っても、サイズ変更は不可能です。どうしても変更したい場合は、別の配列を新たに作成して、そこへデータをコピーしないといけません。
JavaのListクラスを使えば、生成後もフレキシブルに配列のサイズを変更できるので、より無駄が少なく効率的なプログラムが書けます。さらに、Listクラスは多数のデータを管理するための「コレクションクラス」の一種なので、さまざまな便利メソッドも利用できます。
JavaのListクラスは、動的な変更を可能にした「柔軟性がある配列」です。まずはListを使ったプログラムの全体像を把握し、Listクラスの基本的な使い方を見ていきましょう。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listを生成する List<Integer> nums = new ArrayList<Integer>();
// 3つの整数値をListに追加する // (addメソッドの詳細については後述) nums.add(1); nums.add(2); nums.add(3);
// すべての要素をまとめて表示する System.out.println(nums);
// 各要素にアクセスして中身を表示する // (getメソッドの詳細については後述) System.out.println(nums.get(0)); System.out.println(nums.get(1)); System.out.println(nums.get(2)); } } |
//実行結果 |
[1, 2, 3]
1 2 3 |
JavaのListクラスは、宣言や生成、要素へのアクセス方法などが通常の配列とは異なります。Listのデータ型名にはintやchar、floatなどの「基本データ型」は指定できないので、IntegerやFloatなどの「ラッパークラス」が必要です。
最初は少し戸惑うかもしれませんが、サンプルプログラムを進めれば慣れていくはずです。まずは下記3つのポイントから、Listクラスの基本的な使い方を見ていきましょう。
先ほどのサンプルプログラムでは、最初に空のリストを生成して、後から要素の追加を行っていました。要素数が少ない場合は、下記のサンプルプログラムのように、インスタンス化と同時に初期化することも可能です。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // パターンA:asListメソッドを使う(推奨) List<String> listA = new ArrayList<>(Arrays.asList(“A”, “B”, “C”)); // パターンB:初期化時に「インスタンスイニシャライザ」を使う(非推奨) List<String> listB = new ArrayList<String>() { { add(“A”); add(“B”); add(“C”); } }; // 2つのパターンのListを表示する System.out.println(listA); System.out.println(listB); } } |
//実行結果 |
[A, B, C]
[A, B, C] |
本記事で推奨するのはパターンAの「asList」を使う方法です。コンストラクタ呼び出しの「()」に「Arrays.asList」と書き、初期化したい内容を並べると初期化できます。ただし下記のように記載すると、サイズ変更ができない「固定長のList」になるので要注意。「new ArrayList<>」の記載を忘れないようにしてください。
List<String> list = Arrays.asList(“A”, “B”, “C”); // 固定長になってしまう |
パターンBのインスタンスイニシャライザは、インスタンスの生成直後に変数を初期化するための機能。ただし、インスタンスイニシャライザでの初期化は、思わぬバグにつながることがあるためおすすめできません。先ほどの「asList」を使うか、インスタンス生成後にaddメソッドで要素を追加するようにしましょう。
リスト内の任意の要素にアクセスするときは、配列のような「[]」の添え字は使えません。下記のサンプルプログラムのように、getメソッドやsetメソッドを使う必要があります。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listを生成する List<Integer> nums = new ArrayList<Integer>();
// 3つの整数値をListに追加する nums.add(1); nums.add(2); nums.add(3);
// すべての要素をまとめて表示する System.out.println(nums);
// インデックス1の要素を変更する nums.set(1, 100); // インデックス1の要素を表示する System.out.println(nums.get(1)); } } |
//実行結果 |
[1, 2, 3]
100 |
getメソッドやsetメソッドの詳しい使い方は後述します。JavaのListクラスでは、通常の配列のようなインデックス(添え字)でのアクセスや変更ができないので、このあたりは慣れが必要です。
Listに格納したすべての要素に対して、更新や表示などの処理を行いたい場合は、「拡張for文」「forEachメソッド」「iterator(イテレータ)」のいずれかでアクセスできます。Listの全要素にアクセスするサンプルプログラムは下記のとおりです。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.Iterator; // Iteratorの機能を使用する import java.util.List; // Listの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listを生成する List<Integer> nums = new ArrayList<Integer>();
// 3つの整数値をListに追加する nums.add(1); nums.add(2); nums.add(3);
// 「拡張for文」で全要素を表示する for(Integer num : nums) { System.out.println(num); } // 「forEachメソッド」で全要素を表示する(ラムダ式を使用する場合) nums.forEach(num -> System.out.println(num));
// 「forEachメソッド」で全要素を表示する(メソッド参照を使用する場合) nums.forEach(System.out::println);
// 「iterator(イテレータ)」で全要素を表示する(for文の場合) for(Iterator it = nums.iterator(); it.hasNext();) { System.out.println(it.next()); } // 「iterator(イテレータ)」で全要素を表示する(while文の場合) Iterator<Integer> it = nums.iterator(); while(it.hasNext()) { int num = it.next(); System.out.println(num); } } } |
//実行結果 |
1
2 3 1 2 3 1 2 3 1 2 3 1 2 3 |
最もわかりやすいのは、通常の配列と同じ感覚で使える「拡張forループ」でしょう。「for(型名 変数名 : リスト名)」と書くことで、ループ内でひとつずつ要素を取り出して使えます。ただし行数が長くなるため、ソースコードを簡潔にしたい場合は「forEachメソッド」がおすすめです。
forEachメソッドは、通常のforループや拡張forループとは異なり、型名を指定する必要がなく、1行で書けるため効率的なプログラミングができます。forEachメソッド内部では、「ラムダ式」を使用して各要素に行う処理を指定します。変数を使用する必要がない場合は、メソッド名だけを指定する「メソッド参照」を活用すると便利です。
また、Listクラスはコレクションクラスの一種なので、順番にアクセスするためのインターフェイスである「iterator(イテレータ)」も利用できます。今回はfor文とwhile文の例をまとめて紹介しました。いずれもリストからイテレータを取得し、hasNextメソッドで次の要素があるか確認し、nextで実際に要素を取得するという流れは同じです。
JavaのListクラスを便利に使いこなすためには、さまざまなメソッドの機能や活用法を理解しておくことが重要です。本章では下記5種類に分類して、Listクラスのメソッドと応用テクニックを紹介します。
JavaのListクラスは、インスタンス生成後も自由に配列サイズを変更できることが魅力ですが、前述したように要素の扱い方が若干トリッキーです。そのため、まずはListクラスの要素を扱うためのメソッドに慣れておくことが大切です。本章では、とくに重要な下記5つのメソッドについて見ていきましょう。
メソッド名 | get |
引数 | 取得したい要素のインデックス |
戻り値 | リスト内の指定した要素の値 |
「get」は指定したインデックスの値を取得するためのメソッド。JavaのListクラスでは、各要素にアクセスするために「添え字」が使えないので、必ずgetメソッドでアクセスする必要があります。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3));
// それぞれの要素を表示する System.out.println(nums.get(0)); System.out.println(nums.get(1)); System.out.println(nums.get(2)); } } |
//実行結果 |
1
2 3 |
なお、引数に指定するインデックスは配列と同じく、最初の要素が「0」・末尾の要素は「サイズ-1」となります。範囲外を指定すると、「IndexOutOfBoundsException」例外が出るので注意が必要です。
メソッド名 | set |
第1引数 | アクセスしたい要素のインデックス |
第2引数 | 新たに格納する値 |
戻り値 | 変更前の値 |
「set」は任意の要素を書き換えるためのメソッド。先ほどのgetメソッド同様に、リストの要素にアクセスするために必須です。第1引数と第2引数の順番を間違えないように注意してください。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3));
// すべての要素を表示する System.out.println(nums);
// それぞれの要素を書き換える nums.set(0, 100); nums.set(1, 200); nums.set(2, 300);
// すべての要素を表示する System.out.println(nums); } } |
//実行結果 |
[1, 2, 3]
[100, 200, 300] |
また、setメソッドは戻り値として「元の値」を返します。そのため、戻り値をほかの変数で受けると、値のスワップが簡単にできるので便利です。なおgetメソッドと同じように、範囲外を指定すると「IndexOutOfBoundsException」例外が出ます。
メソッド名 | size |
引数 | なし |
戻り値 | リストのサイズ(要素数) |
「size」はリストのサイズ(要素数)を取得するためのメソッド。リストをコピーするときやループを回すときなど、リストの正確なサイズを知りたいときに役立ちます。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3));
// すべての要素を表示する System.out.println(“リストの内容:” + nums);
// リストのサイズを表示する System.out.println(“リストのサイズ:” + nums.size()); } } |
//実行結果 |
リストの内容:[1, 2, 3]
リストのサイズ:3 |
なお、空のリストに対してsizeメソッドを使用すると、結果は「0」となります。ただしリスト変数に「null」を格納している場合は、そのリスト自体にアクセスできないため「NullPointerException」例外が発生することに注意しましょう。
メソッド名 | subList |
第1引数 | 最初のインデックス |
第2引数 | 最後のインデックスの次(要注意) |
戻り値 | コピーしたリスト |
「subList」はリストの一部を切り取り、新しいリストを作成するためのメソッド。検索して該当する部分を抽出したり、ソート後に一部区間を切り取ったりする場合に便利です。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> listA = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
// インデックス1からインデックス3の要素を抽出して別のリストを作成する List<Integer> listB = listA.subList(1, 4); // 第2引数に要注意
// 最初のリストを表示する System.out.println(“最初のリスト:” + listA);
// 切り取ったリストを表示する System.out.println(“切り取ったリスト:” + listB); } } |
//実行結果 |
最初のリスト:[1, 2, 3, 4, 5]
切り取ったリスト:[2, 3, 4] |
このメソッドは引数が少しわかりにくいので注意してください。第1引数は切り取りたい部分の最初のインデックスを指定すればOK。ただし第2引数は、最後のインデックスに「1」を足した数値を指定しないといけません。たとえば、インデックス2からインデックス6を抽出したいのであれば、引数は「2, 7」となります。
メソッド名 | isEmpty |
引数 | なし |
戻り値 | リストが空かどうか |
「isEmpty」はリストが空かどうかを判定するためのメソッド。要素の順番や追加する位置を気にする必要がない場合は、Addメソッドを使うのがおすすめです。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> listA = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
// Integer型の空Listを生成する List<Integer> listB = new ArrayList<>();
// 最初のリストが空かどうか判定する if(listA.isEmpty()) { System.out.println(“最初のリストは空です”); } else { System.out.println(“最初のリストは空ではありません”); } // 2つ目のリストが空かどうか判定する if(listB.isEmpty()) { System.out.println(“2つ目のリストは空です”); } else { System.out.println(“2つ目のリストは空ではありません”); } } } |
//実行結果 |
最初のリストは空ではありません
2つ目のリストは空です |
isEmptyメソッドが「true」を返す変数は、sizeメソッドも「0」を返します。ただし、リスト変数に「null」を格納している場合は「NullPointerException」例外が発生します。
JavaのListクラスの魅力は、インスタンス生成後にいつでも要素を追加できることです。Listクラスでは、要素を追加できる下記2つの便利なメソッドを利用できます。
メソッド名 | add |
引数 | 追加する値 |
戻り値 | 追加に成功したかどうか |
メソッド名 | add |
第1引数 | 挿入する場所(インデックス) |
第2引数 | 挿入する値 |
戻り値 | 挿入に成功したかどうか |
「add」は末尾および指定した場所に要素を追加するためのメソッド。Javaのaddメソッドには上記のように2つのバージョンがあり、1つ目が引数が1つで常に末尾に挿入するもの、2つ目が引数が2つで挿入場所を指定するものです。下記のサンプルプログラムで、それぞれの使い方を確認しましょう。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listを生成する List<Integer> nums = new ArrayList<>();
// Listに3つの値を末尾に追加する nums.add(1); nums.add(2); nums.add(3);
// リストの内容を表示する System.out.println(nums); // Listに場所を指定して3つの値を挿入する nums.add(0,100); // インデックス0に100を挿入する nums.add(3,200); // インデックス3に200を挿入する
// リストの内容を表示する System.out.println(nums); } } |
//実行結果 |
[1, 2, 3]
[100, 1, 2, 200, 3] |
1つ目のaddメソッドは、値を指定すると自動的に末尾に要素が追加されます。2つ目のaddメソッドは、第1引数に挿入したいインデックス、第2引数に挿入したい値を指定します。
ただし、2つ目のaddメソッドの第1引数に指定できる数値は、現在のリストサイズまでなので注意してください。たとえば現在のリストサイズが3であれば、指定できるのは「3」まであり、3を指定した場合は1つ目のaddメソッドと同義になります。4以上を指定すると、「IndexOutOfBoundsException」例外が出ます。
メソッド名 | addAll |
引数 | 追加する値 |
戻り値 | 追加に成功したかどうか |
メソッド名 | addAll |
第1引数 | 挿入する場所(インデックス) |
第2引数 | 挿入する値 |
戻り値 | 挿入に成功したかどうか |
「addAll」は末尾および指定した場所に、別のリストを追加するためのメソッド。JavaのaddAllメソッドも、先ほどのaddメソッド同様に2つの種類があります。1つ目は末尾に、2つ目は任意のインデックスを指定して挿入できます。サンプルプログラムを確認しましょう。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> listA = new ArrayList<>(Arrays.asList(1, 2, 3)); List<Integer> listB = new ArrayList<>(Arrays.asList(4, 5, 6)); List<Integer> listC = new ArrayList<>(Arrays.asList(7, 8, 9));
// ListAの末尾にListBを追加する listA.addAll(listB);
// リストの内容を表示する System.out.println(listA);
// ListAのインデックス3にListCを追加する listA.addAll(3, listC);
// リストの内容を表示する System.out.println(listA); } } |
//実行結果 |
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 7, 8, 9, 4, 5, 6] |
基本的にはaddメソッドと同じです。1つ目のaddAllメソッドは、追加するリストを指定すると自動的に末尾に追加されます。2つ目のaddAllメソッドは、第1引数に挿入したいインデックスを指定します。ただし、現在のリストサイズより大きいインデックスを指定すると、「IndexOutOfBoundsException」例外が出るので注意してください。
JavaのListクラスの魅力は、生成後も自由に要素を削除して、リストのサイズを小さくできることです。Listクラスでは下記4つのメソッドを使用することで、さまざまな方法で要素を削除できます。
メソッド名 | remove |
引数 | 削除する要素のインデックス |
戻り値 | 削除した要素の値 |
「remove」は指定したインデックスの要素を削除するためのメソッド。「何番目の要素を削除したいか」が明確なときは、このメソッドでインデックスを指定して削除すると便利です。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
// リストの内容を表示する System.out.println(nums);
// リストからインデックス1の要素を削除する nums.remove(1);
// リストの内容を表示する System.out.println(nums);
// リストからインデックス3の要素を削除する nums.remove(3);
// リストの内容を表示する System.out.println(nums); } } |
//実行結果 |
[A, B, C, D, E]
[A, C, D, E] [A, C, D] |
removeメソッドは単純で、上記のように削除したい要素のインデックスを指定すれば、自動的にリストと要素の詰め直しが行われます。ただし範囲外のインデックスを指定すると、「IndexOutOfBoundsException」例外が出るので注意が必要です。
なお、インデックスではなく削除したい要素の「値」を指定したい場合は、下記のように「indexOfメソッド」と組み合わせましょう。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(1, 5, 2, 4, 3));
// リストの内容を表示する System.out.println(nums);
// リストから要素「3」のインデックスを取得する int index = nums.indexOf(3);
// インデックスが正常値であれば、リストから要素を削除する if(index != -1) { nums.remove(index); }
// リストの内容を表示する System.out.println(nums); } } |
//実行結果 |
[1, 5, 2, 4, 3]
[1, 5, 2, 4] |
removeメソッドと組み合わせる場合は、事前にif文でインデックスの値を調べておかないと、不正値で例外が出ることがあるので注意してください。indexOfメソッドの使い方については後述します。
メソッド名 | distinct |
引数 | なし |
戻り値 | 新しいストリーム
(toListメソッドでリストに変換する) |
「distinct」はリストから重複要素を取り除き、新たなリストを作成するメソッド。こちらは今までに紹介した通常のメソッドとは異なり、Stream APIに含まれるメソッドなので、メソッドの呼び出しにワンクッション置く必要があります。distinctメソッドの使い方は下記のとおりです。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> listA = new ArrayList<>(Arrays.asList(1, 1, 2, 3, 3));
// 元のリストの内容を表示する System.out.println(listA);
// リストから重複要素を取り除き、新たなリストを作成する List<Integer> listB = listA.stream().distinct().toList();
// 新しいリストの内容を表示する System.out.println(listB); } } |
//実行結果 |
[1, 1, 2, 3, 3]
[1, 2, 3] |
一度の呼び出しですべての重複要素が取り除かれるので、ユニークな要素をリストで管理したいときに便利です。なおdistinctメソッドの呼び出しは、「stream().distinct().toList()」をワンセットで覚えておきましょう。最後の「toList()」は、戻り値をリスト形式で受けるために必要です。
メソッド名 | retainAll |
引数 | ほかのリスト |
戻り値 | 共通要素以外を削除したリスト |
「retainAll」は、ほかのリストと要素を比較し、共通要素以外をすべて削除するためのメソッド。要素の順番や追加する位置を気にする必要がない場合は、Addメソッドを使うのがおすすめです。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> listA = new ArrayList<>(Arrays.asList(1, 2, 3, 3, 5, 6, 8)); // もうひとつのInteger型Listの生成と初期化を行う List<Integer> listB = new ArrayList<>(Arrays.asList(1, 3, 5, 7, 9));
// リストの内容を表示する System.out.println(“リストA” + listA); System.out.println(“リストB” + listB);
// listAからListBとの共通要素以外を削除する listA.retainAll(listB);
// リストの内容を表示する System.out.println(“削除後 ” + listA); } } |
//実行結果 |
リストA[1, 2, 3, 3, 5, 6, 8]
リストB[1, 3, 5, 7, 9] 削除後 [1, 3, 3, 5] |
2つのリストを比較すると、リストAの要素のうち、リストBに含まれない要素すべてが削除されていることが明らかです。あくまで共通項以外を削除するメソッドなので、重複する要素がある場合は保持されます。
メソッド名 | clear |
引数 | なし |
戻り値 | なし |
「Clear」はリストのすべての要素を削除するためのメソッド。要素数がゼロになるため、新たに要素を格納して再利用することも可能です。引数も戻り値もないため、下記のように使い方も非常にシンプルです。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
// リストの内容を表示する System.out.println(“clear前” + nums);
// リストを削除する nums.clear();
// リストの内容を表示する System.out.println(“clear後” + nums); } } |
//実行結果 |
clear前[1, 2, 3, 4, 5]
clear後[] |
リスト内にどれだけ多くの要素が格納されていても、clearメソッドを一度呼ぶだけですべて削除されます。もちろん、sizeメソッドで要素数を確認すると「0」と表示されます。
JavaのListクラスでは、要素を検索するためのメソッドも揃っており、必要に応じて活用すると便利です。下記のメソッドは単体で使うことはもちろん、ほかのメソッドと組み合わせても大きな効果を発揮します。
メソッド名 | contains |
引数 | 検索したい値 |
戻り値 | 指定した値が見つかったかどうか |
「contains」は、指定した値がリスト内に存在するか調べるためのメソッド。リスト内の要素を検索したいシーンはよくありますが、下記のようにcontainsメソッドを使えばすぐに解決できるので便利です。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // String型Listを生成する List<String> list = new ArrayList<>(Arrays.asList(“猫”, “犬”, “虎”, “狼”, “猿”));
// リストから「虎」を検索する if(list.contains(“虎”)) { System.out.println(“虎を発見!”); } else { System.out.println(“虎は見つかりませんでした…”); }
// リストから「ライオン」を検索する if(list.contains(“ライオン”)) { System.out.println(“ライオンを発見!”); } else { System.out.println(“ライオンは見つかりませんでした…”); } } } |
//実行結果 |
虎を発見!
ライオンは見つかりませんでした… |
containsメソッドの戻り値は、「要素を検索できたかどうか」です。戻り値をif文で判定し、trueなら「要素を発見できた」と判断できます。実際のプログラムでは、要素を検出できなかったときに、メッセージの表示やエラー処理などを行うといいでしょう。
メソッド名 | indexOf |
引数 | インデックスを知りたい要素 |
戻り値 | 要素のインデックス
(要素がリストにない場合は「-1」) |
「indexOf」は、指定した要素のインデックスを取得するためのメソッド。このメソッドは多くの場合、ほかのメソッドでインデックスを指定する必要があるものの、「値は分かるがインデックスが不明」というシーンでよく活用されます。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
// 要素「3」のインデックスを取得する int index = nums.indexOf(3);
// 取得したインデックスを表示する System.out.println(index); // 要素「0」のインデックスを取得する index = nums.indexOf(0);
// 取得したインデックスを表示する System.out.println(index); } } |
//実行結果 |
2
-1 |
上記のサンプルプログラムでは、要素「3」と「0」のインデックスを取得しています。しかし「0」という要素はnumsリストに含まれていないため、その結果は「-1」となりました。つまり、リスト内に当該リストがあるかどうかは、「indexOfメソッドの戻り値が-1でないか」を判定すればわかるということです。
先ほどremoveメソッドのサンプルプログラムで、indexOfメソッドと組み合わせたときに「if(index != -1)」とif文で判定していました。これは、そのままremoveにindexOfの戻り値を渡すと、要素がないときに「-1」が渡されて「IndexOutOfBoundsException」例外が出てしまうからです。
メソッド名 | lastIndexOf |
引数 | インデックスを知りたい要素 |
戻り値 | 要素のインデックス
(要素がリストにない場合は「-1」) |
「lastIndexOf」は、指定した要素の最後のインデックスを取得するためのメソッド。指定した要素がリスト内で重複している場合、その最後のインデックスを取得できます。特定の重複要素を変更・削除したい場合は、このメソッドが役立つでしょう。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(1, 1, 2, 3, 1));
// 要素「1」の最後のインデックスを取得する int index = nums.lastIndexOf(1);
// 取得したインデックスを表示する System.out.println(index); // 要素「0」の最後のインデックスを取得する index = nums.lastIndexOf(0);
// 取得したインデックスを表示する System.out.println(index); } } |
//実行結果 |
4
-1 |
基本的な部分は先ほどのindexOfメソッドと同じです。ただし、lastIndexOfは最後のインデックスを返します。そのため、上記のサンプルプログラムで「indexOf(1)」とすると「0」が返りますが、「lastIndexOf(1)」の戻り値は「4」となります。要素が検出されなかった場合、「-1」が返る点はindexOfメソッドと同じです。
Javaのリストをソートしたい場合、基本的には「sortメソッド」で対処できます。しかし、「昇順」と「降順」では手順が異なることや、「reverseメソッド」も便利なことなどから、本章では下記3つのパターンに分けてメソッドの使い方を解説します。
メソッド名 | sort |
引数 | ソートするリスト |
戻り値 | なし |
「sort」はリストの内容をソートするためのメソッド。ソートのアルゴリズムは複雑で、自分で機能を実装するのは大変です。JavaにはListクラスをソートするためのメソッドが備わっているため、要素を並び替える必要があるときはsortメソッドを使うと便利。ただし、sortメソッドはListクラスのメンバではなく、「Collectionsクラス」のメソッドです。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する import java.util.Collections; // Collectionsの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(8, 2, 5, 7, 6, 1, 9, 3, 4));
// ソート前のリストの内容を表示する System.out.println(nums); // リストを昇順でソートする Collections.sort(nums);
// ソート後のリストの内容を表示する System.out.println(nums); } } |
//実行結果 |
[8, 2, 5, 7, 6, 1, 9, 3, 4]
[1, 2, 3, 4, 5, 6, 7, 8, 9] |
上記のように、Collectionクラスのsortメソッドにリストを引き渡すだけで、ソートが完了します。ただしこの方法で可能なのは昇順ソート、つまり小さい順への並び替えだけなので、降順ソートしたい場合は後述する方法が必要です。
また、Listクラスは「参照型変数」なので、sortメソッドに引き渡すと元のリストの内容自体が書き換えられます。元のリストを保持しておきたい場合は、下記のように新しいリスト変数へコピーコンストラクタでコピーしましょう。Listをコピーする方法は後述します。
List<Integer> copy =new ArrayList<>(nums); |
メソッド名 | sort |
第1引数 | ソートするリスト |
第2引数 | ソート方法を定義するコンパレータ
(今回はCollections.reverseOrderで固定) |
戻り値 | なし |
「sort」メソッドでは、大きい順に並べ替える「降順ソート」も行えます。実はCollectionsのsortメソッドにはもうひとつのバージョンがあり、下記のように第2引数に降順ソートを定義する「コンパレータ」を指定すると簡単です。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する import java.util.Collections; // Collectionsの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(8, 2, 5, 7, 6, 1, 9, 3, 4));
// ソート前のリストの内容を表示する System.out.println(nums);
// リストを降順でソートする Collections.sort(nums, Collections.reverseOrder());
// ソート後のリストの内容を表示する System.out.println(nums); } } |
//実行結果 |
[8, 2, 5, 7, 6, 1, 9, 3, 4]
[9, 8, 7, 6, 5, 4, 3, 2, 1] |
先ほどの昇順ソートとの違いは、sortメソッドの第2引数だけです「Collections.reverseOrder()」はいわば降順ソートのアルゴリズムを定義したものであり、「降順ソートのときはこう書く」と覚えておくだけで構いません。
メソッド名 | reverse |
引数 | 逆順に並び替えるリスト |
戻り値 | なし |
「reverse」はリストを逆順に並び替えるためのメソッド。逆順に並び替えたいときに便利なメソッドですが、実は下記のように昇順ソートしてからreverseメソッドを使用することでも、降順ソートを実現することができます。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する import java.util.Collections; // Collectionsの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(8, 2, 5, 7, 6, 1, 9, 3, 4));
// ソート前のリストの内容を表示する System.out.println(nums);
// リストを昇順でソートする Collections.sort(nums);
// ソート後のリストの内容を表示する System.out.println(nums);
// リストを逆順に並び替える Collections.reverse(nums);
// ソート後のリストの内容を表示する System.out.println(nums); } } |
//実行結果 |
[8, 2, 5, 7, 6, 1, 9, 3, 4]
[1, 2, 3, 4, 5, 6, 7, 8, 9] [9, 8, 7, 6, 5, 4, 3, 2, 1] |
一度昇順ソートを行ってから、今度は降順で並び替えたいというシーンもあります。そんな場合はsortメソッドをもう一度呼び出すよりも、reverseメソッドにするほうが実行速度が速くなるのでおすすめです。
JavaのListはクラスなので、基本データ型ではなく「参照型変数」となります。つまり、List変数をメソッドに渡すと、元のリストが変更される可能性があるということです。下記のサンプルプログラムで状況を確認してみましょう。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3));
// 最初のリストの内容を表示する System.out.println(“最初” + nums);
// 内部でリストを編集する関数を呼び出す test(nums);
// もう一度リストの内容を表示する System.out.println(“メソッド呼び出し後:” + nums); }
public static void test(List<Integer> list) { // メソッド内でリストの内容を変更する list.remove(0); // リストの先頭要素を取り除く list.add(100); // リストの末尾に要素「100」を追加する
// メソッド内でのリストの内容を表示する System.out.println(“メソッド内:” + list); } } |
//実行結果 |
最初[1, 2, 3]
メソッド内:[2, 3, 100] メソッド呼び出し後:[2, 3, 100] |
上記のように、mainメソッド内で最初のリスト変数に対する変更を行っていないにも関わらず、メソッド呼び出し後に内容が変更されています。これは、testメソッド内で引数として渡したリストを変更しているからです。つまり、mainの「nums」とtestの「list」は同じものを指しているということです。
こうしたJavaの挙動は、思わぬバグにつながることがあるので要注意。Javaでは、参照型変数をメソッドへ引き渡したときは、「常に変更される可能性がある」くらいに考えておくのが賢明です。そこで、どうしても「元の内容を保持したい」という場合は、下記のようにコピーコンストラクタで「ディープコピー」を行っておきましょう。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3));
// 最初のリストの内容を表示する System.out.println(“最初” + nums);
// 新しいリスト変数に「ディープコピー」する List<Integer> copy = new ArrayList<>(nums);
// 内部でリストを編集する関数を呼び出す test(copy);
// もう一度リストの内容を表示する System.out.println(“メソッド呼び出し後:” + nums); }
public static void test(List<Integer> list) { // メソッド内でリストの内容を変更する list.remove(0); // リストの先頭要素を取り除く list.add(100); // リストの末尾に要素「100」を追加する
// メソッド内でのリストの内容を表示する System.out.println(“メソッド内:” + list); } } |
//実行結果 |
最初[1, 2, 3]
メソッド内:[2, 3, 100] メソッド呼び出し後:[1, 2, 3] |
なお、「List<Integer> copy = nums;」のように書くと、ディープコピーではなくシャローコピーになるため、結局書き換えられてしまいます。また、Listは「Cloneable」ではないため、「cloneメソッド」は使えません。コピーコンストラクタによるコピーはよく使う手法なので、必ず覚えておきましょう。
JavaのListクラスはさまざまな機能が使えて便利ですが、「通常の配列も使いたい」ときもあるでしょう。そんなときは配列とリストの相互変換ができると、より効率的なプログラムが書けます。本章では、配列とリストを相互変換する方法を、下記2つのパターンに分けて解説します。
通常の配列からリストへ変換するときは、「Arrays.asListメソッド」を使用します。ただし、asListメソッドは「リストのサイズ変更が必要か」どうかによって、使い方が異なるので注意が必要です。サンプルプログラムで詳細を確認しましょう。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // String型配列の生成と初期化を行う String[] array = {“AAA”, “BBB”, “CCC”};
// arrayの内容を表示する System.out.println(“array:” + Arrays.toString(array));
// 上記の配列をString型リスト「listA」に変換する List<String> listA = Arrays.asList(array);
// listAの内容を表示する System.out.println(“listA:” + listA);
// しかし、リストのサイズを変更しようとすると、 // 「UnsupportedOperationException」が出る! // コメントアウトして試してみてください //listA.add(“DDD”);
// サイズを変更可能なリストにするためにコンストラクタを使用する // 上記の配列をString型リスト「listB」に変換する // 実は、これは冒頭で紹介した「リストの初期化方法」と同じこと List<String> listB = new ArrayList<>(Arrays.asList(array));
// listBの内容を表示する System.out.println(“listB:” + listB);
// このように、リストのサイズ変更も問題なく可能! listB.add(“DDD”); // listBの内容を表示する System.out.println(“listB:” + listB); } } |
//実行結果 |
array:[AAA, BBB, CCC]
listA:[AAA, BBB, CCC] listB:[AAA, BBB, CCC] listB:[AAA, BBB, CCC, DDD] |
重要なポイントは、listAが「Arrays.asList(array)」、listBが「new ArrayList<>(Arrays.asList(array))」と記載して変数を生成していることです。両者の意味合いはまったく異なります。「Arrays.asList()」は「new ArrayList<>()」の代替ではありません。
listAはasListメソッドにより、単に配列をリスト形式に変換しています。この方法でもリストとして機能しますが、「固定サイズのList」になることに要注意。つまり、リストのサイズを変更できず、通常の配列と大差ないということ。もしaddメソッドやremoveメソッドでサイズを変更しようとすると、「UnsupportedOperationException」例外が出ます。
一方でlistBは、asListで固定サイズのListに変換したものを、ArrayListクラスのコピーコンストラクタに代入しています。実は、これは今までのサンプルプログラムで、リストを初期化してきた方法と同じです。こうすることにより、配列を「可変長のList」として正確に変換できます。
listAの方法は、Listクラスの便利なメソッドを使いたいものの、サイズ変更の必要はない場合におすすめです。一方で、可変長のリストとして使いたい場合は、listBの手法が必須です。
リストから通常の配列へ変換するときは、「toArrayメソッド」を使用します。ただし、事前にリストと同じサイズの配列を生成し、toArrayメソッドに引き渡す必要があるので注意が必要です。サンプルプログラムで詳細を確認しましょう。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // String型List配列の生成と初期化を行う List<String> list = new ArrayList<>(Arrays.asList(“AAA”, “BBB”, “CCC”));
// listの内容を表示する System.out.println(“list:” + list);
// リストのデータを格納するための配列を生成する // 配列のサイズは必ずリストのsizeメソッドで指定する String[] array = new String[list.size()];
// リストをString型配列に変換する list.toArray(array);
// arrayの内容を表示する System.out.println(“array:” + Arrays.toString(array)); } } |
//実行結果 |
list:[AAA, BBB, CCC]
array:[AAA, BBB, CCC] |
配列への変換自体は、toArrayメソッドに配列変数を渡すだけなので単純です。ただし、その前に自身で「同じ型」「同じサイズ」の配列変数を作成しておく必要があります。変数の型が一致しない場合は、「ArrayStoreException」例外が出ます。また、toArrayメソッドの引数は省略できますが、その場合はObject型の配列が返るので不便です。
Javaでは二次元配列や三次元配列など、多次元配列を生成できますが、Listクラスには二次元以上のバージョンは用意されていません。しかし、宣言やアクセスの方法を工夫すれば、Listでも「多次元配列」と同じものを再現できます。サンプルプログラムで詳細を確認しましょう。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.List; // Listの機能を使用する public class Test {
public static void main(String[] args) { int numX = 10; // 一次元方向の要素数 int numY = 5; // 二次元方向の要素数 int count = 0; // 要素カウント用変数 // ジェネリクスの型名をList<>にして二次元リストを宣言する List<List<Integer>> list = new ArrayList<>(); // 二次元目の要素をnumY個生成する for(int i = 0; i < numY; i++) { // リスト内にリストを生成していくイメージ list.add(new ArrayList<Integer>()); } // 先ほど生成した二次元目に一次元目を追加していく for(List<Integer> y : list) { // 一次元目の要素をnumX個生成する for(int i = 0; i < numX; i++) { y.add(count++); // 要素のデータは順番に1ずつ増やす } } // 二重の拡張forループで二次元リストの全要素を表示する for(List<Integer> y : list) { for(Integer x : y) { System.out.printf(“%2d “, x); // 書式設定で2桁表示にする } System.out.println(); // 改行する }
System.out.println(); // 改行する // 適当な場所の要素を「-1」にする // まずgetで二次元目を取得し、次にsetで一次元目のインデックスを値を指定 // イメージを掴むためにいろいろ変更してみてください list.get(1).set(2, -1); // 配列形式ならlist[1][2] = -1 list.get(2).set(8, -1); // 配列形式ならlist[2][8] = -1 list.get(3).set(6, -1); // 配列形式ならlist[3][6] = -1 list.get(4).set(0, -1); // 配列形式ならlist[4][0] = -1 // 改めてすべての要素を表示する for(List<Integer> y : list) { for(Integer x : y) { System.out.printf(“%2d “, x); // 書式設定で2桁表示にする } System.out.println(); // 改行する } } } |
//実行結果 |
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 0 1 2 3 4 5 6 7 8 9 10 11 -1 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 -1 29 30 31 32 33 34 35 -1 37 38 39 -1 41 42 43 44 45 46 47 48 49 |
上記のサンプルプログラムの詳細は、ひとつずつ処理を追って確認してみましょう。大まかな流れは、型名に「リスト」を指定してインスタンスを作成し、二次元目→一次元目の順番で要素を追加していくというものです。細かな部分で工夫が必要になりますが、三次元以上の多次元配列も同様の手順で作成できます。
Javaのリストには、大きく分けて「List」と「ArrayList」の2種類のものがあります。両者の大きな違いは、Listはクラスではなく「インターフェイス」なのでインスタンスを生成できず、ArrayListはクラスなのでインスタンスを生成できるという点です。これまで、List変数のインスタンスを生成するとき、常に下記のように書いてきました。
List<Integer> list = new ArrayList<>(); |
これを「List<Integer> list = new List<>();」と書くことはできません。List型ではインスタンスを生成できないからです。上記のコードには、「ArrayList型で生成したリストのインスタンスを、List型変数に代入する」という意味合いがあります。もちろん、下記のように変数をArrayList型で宣言することも可能です。
ArrayList<Integer> list = new ArrayList<>(); |
上記のように宣言すると、Listクラスのメソッドに加えて、ArrayListのメソッドも利用できるようになります。たとえば、ArrayListには「cloneメソッド」があるため、下記のサンプルプログラムのようにcloneメソッドを使用すると、リストを簡単にディープコピーできるので便利です。
//サンプルプログラム |
import java.util.ArrayList; // ArrayListの機能を使用する
import java.util.Arrays; // Arraysの機能を使用する public class Test {
public static void main(String[] args) { // Integer型Listの生成と初期化を行う ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3)); // 最初のリストの中身を表示する System.out.println(list);
// cloneメソッドでリストをディープコピーする ArrayList<Integer> copy = (ArrayList<Integer>) list.clone();
// コピーしたリストの中身を表示する System.out.println(copy); } } |
//実行結果 |
[1, 2, 3]
[1, 2, 3] |
なお、Listをcloneメソッドでコピーすると戻り値はObject型になるため、それをリストの型名でキャストする必要があります。
JavaのListクラスはいわば「柔軟性の高い配列」で、インスタンス生成後も自由に変更できることが魅力です。通常の配列とは使い方が少し異なりますが、Listでしか使えない便利メソッドも多いため、うまく使いこなすと今までより大幅にプログラムの効率が高まります。一部のメソッドは難しい知識が必要になりますが、少しずつ慣れていきましょう。
2024.06.17
子供におすすめのプログラミングスクール10選!学習メリットや教室選びのコツも紹介
#プログラミングスクール
2022.01.06
【完全版】大学生におすすめのプログラミングスクール13選!選ぶコツも詳しく解説
#プログラミングスクール
2024.01.26
【未経験でも転職可】30代におすすめプログラミングスクール8選!
#プログラミングスクール
2024.01.26
初心者必見!独学のJava学習方法とおすすめ本、アプリを詳しく解説
#JAVA
2024.01.26
忙しい社会人におすすめプログラミングスクール15選!失敗しない選び方も詳しく解説
#プログラミングスクール
2022.01.06
【無料あり】大阪のおすすめプログラミングスクール14選!スクール選びのコツも紹介
#プログラミングスクール
2024.01.26
【目的別】東京のおすすめプログラミングスクール20選!スクール選びのコツも徹底解説
#プログラミングスクール
2024.01.26
【無料あり】福岡のおすすめプログラミングスクール13選!選び方も詳しく解説
#プログラミングスクール
2024.01.26
【徹底比較】名古屋のおすすめプログラミングスクール13選!選び方も詳しく解説
#プログラミングスクール
2024.01.26
【徹底比較】おすすめのプログラミングスクール18選!失敗しない選び方も徹底解説
#プログラミングスクール