リスト

目次
  • 解説
  • 1. 学習の準備
  • 2. リストとは
  • 3. リストの宣言
  • 4. リストと要素数・ループ
  • 5. リストの操作
問題に挑戦!
進捗を変更する




解説

1. 学習の準備


下表のとおり学習用のクラスを作成して、その中にmainメソッドを作っておきましょう。

ファイル名ListAndMap.java
パッケージ名(格納フォルダー)net.digskill
クラス名ListAndMap

2. リストとは


配列宣言時に要素数が確定し、追加や削除ができないという制約がありました。これを解消する仕組みがリストです。リストはクラスやインターフェースの仕組みを活用していますので、完全に理解するのは難しいです。今は使い方だけを学習し、詳しい仕組みは今後で構いません。

配列リストで同じ処理を並べて動きや違いを見てみましょう。

    static void practiceList() {
        String[] stringArray = new String[2];               // 宣言
        stringArray[0] = "いぬ";         // 代入
        stringArray[1] = "ねこ";         // 代入
        Logger.out(stringArray[1]);      // 参照
        stringArray[1] = "猫";           // 再代入
        Logger.out(stringArray[1]);      // 参照

        List<String> stringList = new ArrayList<String>();  // 宣言
        stringList.add("いぬ");          // 要素追加
        stringList.add("ねこ");          // 要素追加
        Logger.out(stringList.get(1));   // 参照
        stringList.set(1, "猫");         // 要素内容変更
        Logger.out(stringList.get(1));   // 参照
        stringList.add("にわとり");      // 要素追加
        Logger.out(stringList.get(2));   // 参照
        stringList.remove(0);            // 要素削除
        Logger.out(stringList.get(0));   // 前に詰められたため「猫」
        Logger.out(stringList.get(2));   // 前に詰められたためエラー
    }
[2020/12/31 12:34:56.789] ねこ      // 配列の2番目の要素
[2020/12/31 12:34:56.789] 猫        // 再代入された配列の2番目の要素
[2020/12/31 12:34:56.789] ねこ      // リストの2番目の要素
[2020/12/31 12:34:56.789] 猫        // 内容変更されたリストの2番目の要素
[2020/12/31 12:34:56.789] にわとり  // リストに追加された3番目の要素
[2020/12/31 12:34:56.789] 猫        // 1番目の要素が削除され前に詰められた要素
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 2 out of bounds for length 2
        at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
        at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
        at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
        at java.base/java.util.Objects.checkIndex(Objects.java:372)
        at java.base/java.util.ArrayList.get(ArrayList.java:459)
        at net.digskill.ListAndMap.practiceList(ListAndMap.java:31)
        at net.digskill.ListAndMap.main(ListAndMap.java:10)

宣言時に要素数を指定していないこと、要素の追加・削除ができることなどの違いが見えます。

3. リストの宣言


リストの宣言は以下のように行います。

まず「List」というのは、「配列のように同じ変数(要素)をたくさん持てること」や、「要素を追加・削除できること」、「拡張for文で列挙できること」などの「ルール」を持った「インターフェース」です。インターフェースはルールだけで中身がありません。

右辺の「ArrayList」というのは、Listインターフェースのルールをきちんと守っている、中身を持ったクラスです。つまり上図では、Listインターフェースのルールを守ったクラスの実体を入れる変数stringListを宣言し、そこにArrayListクラスの実体を代入している、ということになります。

この見慣れない「<>」では、各要素がString型であることを明示しています。これによって、この変数の各要素をString型として扱うことが可能となり、他の型を代入しようとするとエラーとなります。右辺の「<>」の中身は省略することが可能です。

なお、「<>」の中には基本型を指定できません。そのため、数値ならばInteger型Long型、論理値ならばBoolean型、というようにラッパークラスで型を指定してください。

4. リストと要素数・ループ


リスト配列と同様に、添字が0から始まり要素数 - 1までという特徴を持っているため、3種類のループ(while, for, 拡張for)による列挙が可能です。なお、要素数sizeメソッドにより取得できます。

    static void practiceListLoop() {
        List<String> list = new ArrayList<String>();
        list.add("いぬ");
        list.add("ねこ");
        list.add("にわとり");

        // while文
        int listIdx = 0;
        while (listIdx < list.size()) {    // sizeで要素数取得
            Logger.out(list.get(listIdx)); // getに添字を指定して要素取得
            listIdx++;
        }

        // for文
        for (int idx = 0; idx < list.size(); idx++) {
            Logger.out(list.get(idx));     // getに添字を指定して要素取得
        }

        // 拡張for文
        for (String element : list) {      // 要素を順番に取得
            Logger.out(element);
        }
    }

5. リストの操作


配列では添字を使って各要素を指定して代入や参照を行いましたね。リストはArrayListというクラスが持っているメソッドなどを使用して次のような操作が行えます。

  • 追加:addメソッド
  • 変更:setメソッド
  • 取得:getメソッド
  • 削除:removeメソッド
  • 全削除:clearメソッド
  • 並び替え:Collectionsのsortメソッド
  • 含まれているか:containsメソッド

追加:addメソッド

addメソッドでは、丸括弧に入れる値(以下、引数と呼びます)をListの末尾に追加します。引数の1つ目に添字を指定した場合には、その位置に挿入されます。

なお、同じ値の要素を重複して追加することが可能です。

    static void practiceListAdd() {
        List<String> list = new ArrayList<>();
        list.add("い");    // 末尾に追加
        list.add("え");    // 末尾に追加
        list.add(0, "あ"); // 1番目に挿入
        list.add(2, "う"); // 3番目に挿入
        list.add("お");    // 末尾に追加
        list.add("い");    // 末尾に追加
        for (String element : list) {
            Logger.out(element);
        }
    }
[2020/12/31 12:34:56.789] あ
[2020/12/31 12:34:56.789] い // 重複して追加が可能
[2020/12/31 12:34:56.789] う
[2020/12/31 12:34:56.789] え
[2020/12/31 12:34:56.789] お
[2020/12/31 12:34:56.789] い // 最後に追加した「い」

変更:setメソッド

setメソッドでは、引数の1つ目の添字で指定した要素の値を、引数の2つ目で指定した値に変更します。

    static void practiceListSet() {
        // 宣言時に要素を追加する方法を使用
        List<Integer> list = new ArrayList<>(Arrays.asList(10, 20, 30));
        list.set(1, 200);     // 2番目を200に変更
        list.set(0, 1000);    // 1番目を1000に変更
        for (Integer element : list) {
            Logger.out(element);
        }
    }
[2020/12/31 12:34:56.789] 1000
[2020/12/31 12:34:56.789] 200
[2020/12/31 12:34:56.789] 30

取得:getメソッド

getメソッドでは、引数で添字を指定した要素の値を取得します。存在しない添字を指定するとエラーが発生します。

    static void practiceListGet() {
        // 宣言時に要素を追加する方法を使用
        List<String> list =
            new ArrayList<>(Arrays.asList("あ", "い", "う", "え", "お"));
        String element3rd = list.get(2); // 3番目の要素の値を取得
        Logger.out(element3rd);
        String element5th = list.get(4); // 5番目の要素の値を取得
        Logger.out(element5th);
        String element7th = list.get(6); // 7番目の要素の値を取得
        Logger.out(element7th);          // エラー
    }
[2020/12/31 12:34:56.789] う
[2020/12/31 12:34:56.789] お
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 6 out of bounds for length 5
        at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
        at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
        at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
        at java.base/java.util.Objects.checkIndex(Objects.java:372)
        at java.base/java.util.ArrayList.get(ArrayList.java:459)
        at net.digskill.ListAndMap.practiceListGet(ListAndMap.java:159)
        at net.digskill.ListAndMap.main(ListAndMap.java:17)

削除:removeメソッド

removeメソッドでは、引数にint型の値を指定した場合はその添字に対応する要素を削除し、引数に要素と同じ型の値を指定した場合には、その型のequalsメソッドで先頭から比較して、最初に一致した要素を削除します。

    static void practiceListRemove() {
        // 宣言時に要素を追加する方法を使用
        List<String> list =
            new ArrayList<>(Arrays.asList("あ", "い", "う", "え", "い"));
        list.remove(3);     // 4番目を削除
        list.remove("い");  // 要素の中身を指定して削除
        for (String element : list) {
            Logger.out(element);
        }
    }
[2020/12/31 12:34:56.789] あ
[2020/12/31 12:34:56.789] う
[2020/12/31 12:34:56.789] い // 2つ目の「い」は残っている

全削除:clearメソッド

clearメソッドでは、要素をすべて削除します。

    static void practiceListClear() {
        // 宣言時に要素を追加する方法を使用
        List<String> list =
            new ArrayList<>(Arrays.asList("あ", "い", "う", "え", "お"));
        list.clear();
        for (String element : list) {
            Logger.out(element);
        }
        Logger.out("要素が無いため出力も無い");
    }
[2020/12/31 12:34:56.789] 要素が無いため出力も無い

並び替え:Collectionsのsortメソッド

Collectionsクラスsortメソッドにより、要素を昇順(小さい順)で並び替えることが可能です。降順(大きい順)で並び替えることはできませんので、先にsortで昇順にしてから、同じくCollectionsクラスreverseメソッドにより、逆順に並び替えましょう。

    static void practiceListSort() {
        // 宣言時に要素を追加する方法を使用
        List<Integer> list = new ArrayList<>(Arrays.asList(30, 10, 20));
        Collections.sort(list);     // 昇順に並び替える
        for (Integer element : list) {
            Logger.out(element);
        }
        Collections.reverse(list);  // 逆順に並び替える
        for (Integer element : list) {
            Logger.out(element);
        }
    }
[2020/12/31 12:34:56.789] 10
[2020/12/31 12:34:56.789] 20
[2020/12/31 12:34:56.789] 30
[2020/12/31 12:34:56.789] 30
[2020/12/31 12:34:56.789] 20
[2020/12/31 12:34:56.789] 10

ArrayListクラスのsortメソッドは使い方が難しいため、当面はこちらを使いましょう。

含まれているか:containsメソッド

containsメソッドでは、要素の型のequalsメソッドで先頭から比較して、一致する要素が見つかった場合はtrueを、見つからなかった場合はfalseを返します。

同じ値の要素を重複して追加したくない場合などに便利です。

    static void practiceListContains() {
        List<String> list = new ArrayList<>();
        Logger.out(list.contains("あ")); // 「あ」が含まれていない
        list.add("あ");                  // 末尾に追加
        Logger.out(list.contains("あ")); // 「あ」が含まれている
        if (!list.contains("い")) {
            list.add("い");         // 含まれていなければ末尾に追加
        }
        if (!list.contains("い")) {
            list.add("い");         // 含まれていなければ末尾に追加
        }
        for (String element : list) {
            Logger.out(element);
        }
    }
[2020/12/31 12:34:56.789] false    // まだ追加していない
[2020/12/31 12:34:56.789] true     // 追加後
[2020/12/31 12:34:56.789] あ
[2020/12/31 12:34:56.789] い       // 1回しか追加されていない

問題

確認問題


確認問題1

次のコードを実行した際の出力結果はどれか。

    List<Integer> list = new ArrayList<>();
    list.add(4);
    list.add(1);
    list.add(3);
    list.add(1, 2);
    list.add(0, 5);
    Collections.reverse(list);
    if (!list.contains(1)) {
        list.add(1);
    }
    list.add(3);
    list.remove(3);
    String result = "|";
    for (Integer element : list) {
        result += element + "|";
    }
    Logger.out(result);