Java Silver Javaの基本データ型と文字列の操作 問題

ゆめさくが作った練習問題です。本試験の問題ではありません。

Javaの基本データ型と文字列の操作の問題を1⁠6問、小分野ごとにまとめました。選ぶと、その場で正誤と解説が出ます。

ログインなしで解けます。答えはこのブラウザの中だけに残り、どこにも送りません。

この分野の小分野

  • 変数とスコープと var と文字列(8問)
  • 配列と ArrayList(8問)

Javaの基本データ型と文字列の操作の問題

16 問

選ぶと、すぐに正誤と解説が出ます。選んだ答えは変えられません。

  1. 変数とスコープと var と文字列

    問 1次の宣言のうち、コンパイルできるものはどれですか。

  2. 問 2次のコードを引数なしで実行したときの結果として正しいものはどれですか。

    public class Main {
        static int total;
    
        public static void main(String[] args) {
            int count;
            if (args.length > 0) {
                count = args.length;
            } else {
                count = -1;
            }
            int unused;                          // (A)
            System.out.println(total + count);   // (B)
        }
    }
  3. 問 3次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        public static void main(String[] args) {
            int n = 1;
            {
                int m = n + 1;
                System.out.print(m);
            }
            int m = 10;                    // (A)
            for (int i = 0; i < 2; i++) {
                int n = i;                 // (B)
                System.out.print(n + m);
            }
        }
    }
  4. 問 4次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        public static void main(String[] args) {
            var var = 3;                  // (A)
            var total = var * 2L;         // (B)
            var a = total, b = 1;         // (C)
            System.out.println(a + b);
        }
    }
  5. 問 5次のコードを実行したときの出力として正しいものはどれですか。

    public class Main {
        public static void main(String[] args) {
            String a = "  ";
            String b = "";
            System.out.println(a.isEmpty() + " " + a.isBlank() + " " + b.isBlank() + " " + "ab".repeat(2).length());
        }
    }
  6. 問 6次のコードを実行したときの結果として正しいものはどれですか。

    public class Main {
        public static void main(String[] args) {
            String s = "yumesaku";
            int i = s.indexOf('u', 2);
            System.out.print(i + " ");
            System.out.print(s.charAt(s.length()));
        }
    }
  7. 問 7次のコードを実行したときの出力として正しいものはどれですか。

    public class Main {
        public static void main(String[] args) {
            String s = "x";
            s += 1 + 2;
            s = s + 1 + 2;
            System.out.println(s);
        }
    }
  8. 問 8次のコードを実行したときの出力として正しいものはどれですか。

    public class Main {
        public static void main(String[] args) {
            String t = """
                ab
                  c\
                d
                """;
            System.out.print(t.length() + "[" + t.replace(" ", "_").replace("\n", "|") + "]");
        }
    }
  9. 配列と ArrayList

    問 9次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        public static void main(String[] args) {
            int[] a, b[];
            int c[], d;
            a = new int[2];
            b = new int[2][];          // (A)
            d = 5;                     // (B)
            c = a;
            System.out.println(a.length + b.length + d + c.length);
        }
    }
  10. 問 10次のコードを実行したときの結果として正しいものはどれですか。

    import java.util.*;
    
    public class Main {
        public static void main(String[] args) {
            List<String> fixed = List.of("a", "b");
            List<String> copy = new ArrayList<>(fixed);
            copy.add("c");
            System.out.print(copy.size());
            fixed.add("c");   // (A)
        }
    }
  11. 問 11次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        static int sum(int[] a) {
            int s = 0;
            for (int x : a) {
                s += x;
            }
            return s;
        }
    
        public static void main(String[] args) {
            int[] x = {1, 2, 3,};                      // (A)
            System.out.print(sum(x));
            System.out.print(sum(new int[]{4, 5}));   // (B)
        }
    }
  12. 問 12次のコードを実行したときの結果として正しいものはどれですか。

    public class Main {
        public static void main(String[] args) {
            int[] a = new int[3];
            for (int i = 0; i <= a.length; i++) {
                a[i] = i * 2;
                System.out.print(a[i]);
            }
        }
    }
  13. 問 13次のコードを実行したときの出力として正しいものはどれですか。

    public class Main {
        public static void main(String[] args) {
            int[][] g = {{1, 2, 3}, {4}, {}};
            int total = 0;
            for (int[] row : g) {
                total += row.length;
            }
            System.out.println(g.length + " " + total + " " + g[1][0]);
        }
    }
  14. 問 14次のコードを実行したときの出力として正しいものはどれですか。

    import java.util.*;
    
    public class Main {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("a");
            list.add("c");
            list.add(1, "b");
            String old = list.set(2, "d");
            list.remove("a");
            System.out.println(old + list + list.size());
        }
    }
  15. 問 15java.util.* を import しているとき、次の文のうちコンパイルエラーになるものはどれですか。

  16. 問 16次のコードを実行したときの結果として正しいものはどれですか。

    import java.util.*;
    
    public class Main {
        public static void main(String[] args) {
            List<Integer> nums = new ArrayList<>(List.of(1, 2, 3, 4));
            for (Integer n : nums) {
                if (n == 2) {
                    nums.remove(n);   // (A)
                }
            }
            System.out.println(nums);
        }
    }

16 問中 0 問に答えました

解説の一覧

解いたあとに読み返せるよう、全問の解説を並べています。問いを開くと解説が出ます。

変数とスコープと var と文字列

問1次の宣言のうち、コンパイルできるものはどれですか。

正解は「int n = 0b1⁠1⁠1⁠1_0⁠0⁠0⁠0;」です。0b で始まる数は 2 進数のリテラルで、0b1⁠1⁠1⁠1_0⁠0⁠0⁠0 は 1⁠0 進数の 2⁠4⁠0 です。数字と数字の間のアンダースコアは読みやすくするための区切りで、値には影響しません。2⁠4⁠0 は int の範囲に収まるのでコンパイルできます。long n = 3⁠0⁠0⁠0⁠0⁠0⁠0⁠0⁠0⁠0; は、末尾に L が無いので 3⁠0⁠0⁠0⁠0⁠0⁠0⁠0⁠0⁠0 は int のリテラルとして扱われます。int の最大値 2⁠1⁠4⁠7⁠4⁠8⁠3⁠6⁠4⁠7 を超えるので、代入先が long でもコンパイルエラーになります。3⁠0⁠0⁠0⁠0⁠0⁠0⁠0⁠0⁠0L と書けば通ります。byte n = 0x8⁠0; の 0x8⁠0 は 1⁠6 進数で 1⁠2⁠8 です。byte の範囲は -1⁠2⁠8 から 1⁠2⁠7 なので収まらず、エラーになります。short n = 4⁠0_0⁠0⁠0; も、short の範囲 -3⁠2⁠7⁠6⁠8 から 3⁠2⁠7⁠6⁠7 を超えるのでエラーになります。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠3.html#jls-⁠3.1⁠0.1

問2次のコードを引数なしで実行したときの結果として正しいものはどれですか。
public class Main {
    static int total;

    public static void main(String[] args) {
        int count;
        if (args.length > 0) {
            count = args.length;
        } else {
            count = -1;
        }
        int unused;                          // (A)
        System.out.println(total + count);   // (B)
    }
}

正解は「-1 と出力される」です。ローカル変数は、値を読む前に必ず代入されていなければなりません。コンパイラはこれを、どの道筋を通っても代入済みかどうかで調べます。count は if の側でも else の側でも代入されているので、if 文のあとでは必ず値が入っていて、(B) で読んでも問題ありません。static なフィールドの total は、初期化を書かなくても既定値 0 が入ります。引数なしなので args.length は 0 で else に進み、count は -1、出力は 0 + -1 で -1 です。(A) の unused は初期化していませんが、一度も読んでいないのでエラーになりません。エラーになるのは、代入前に値を読んだときです。(B) でエラーになるという選択肢は、else でも代入していることを見落とした誤りです。0 と出力されるという選択肢は、count が使われずに total だけが出ると考えた誤りです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠6.html

問3次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    public static void main(String[] args) {
        int n = 1;
        {
            int m = n + 1;
            System.out.print(m);
        }
        int m = 10;                    // (A)
        for (int i = 0; i < 2; i++) {
            int n = i;                 // (B)
            System.out.print(n + m);
        }
    }
}

正解は「(B) の行でコンパイルエラーになる」です。ローカル変数の有効範囲 (スコープ) は、宣言した場所から、それを囲むブロックの終わりまでです。そして、ある名前のローカル変数のスコープの中で、同じ名前のローカル変数をもう一度宣言するとコンパイルエラーになります。n は main の先頭で宣言されているので、for 文の中もまだ n のスコープです。そこで int n = i; と宣言している (B) の行がエラーになります。(A) の int m = 1⁠0; は問題ありません。最初の m は波かっこのブロックの中で宣言され、ブロックが閉じた時点でスコープが終わっているので、同じ名前をもう一度使えます。2⁠1⁠0⁠1⁠1 と 1⁠0⁠1⁠1 は、(B) が許されると考えた場合の出力です。内側のブロックで外の変数と同じ名前を付けて隠せるのはフィールドとの関係で、ローカル変数どうしではできません。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠6.html#jls-⁠6.4

問4次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    public static void main(String[] args) {
        var var = 3;                  // (A)
        var total = var * 2L;         // (B)
        var a = total, b = 1;         // (C)
        System.out.println(a + b);
    }
}

正解は「(C) の行でコンパイルエラーになる」です。var を使う宣言では、1 つの文で宣言できる変数は 1 つだけです。int a = 1, b = 2; のようにコンマで並べる書き方は、var では許されません。変数ごとに型を推論するので、まとめた宣言の型が決められないためです。そのため (C) の行で、var は複数の宣言では使えないというコンパイルエラーになります。(A) の var var = 3; は正しい文です。var はどこでも使えない予約語ではなく、ローカル変数の型の位置でだけ特別な意味を持つ語なので、変数の名前には使えます。(B) の var total = var * 2L; は、int の 3 と long の 2L を掛けるので結果は long になり、total の型は long と推論されます。7 と出力されるという選択肢は、(C) が許されると考えた場合の結果 (6 + 1) です。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠4.html#jls-⁠1⁠4.4

問5次のコードを実行したときの出力として正しいものはどれですか。
public class Main {
    public static void main(String[] args) {
        String a = "  ";
        String b = "";
        System.out.println(a.isEmpty() + " " + a.isBlank() + " " + b.isBlank() + " " + "ab".repeat(2).length());
    }
}

正解は「false true true 4」です。isEmpty は文字数が 0 のときだけ true を返します。a は空白 2 文字なので長さは 2 で、a.isEmpty() は false です。isBlank は、空の文字列か、空白の文字だけでできた文字列のとき true を返します (Java 1⁠1 で追加)。a は空白だけなので true、b は空なので true です。repeat は文字列を指定した回数くり返した新しい文字列を返すので、"ab".repeat(2) は abab で、長さは 4 です。true true true 4 は、空白だけの文字列でも isEmpty が true になると考えた誤りです。false true false 4 は、空の文字列は isBlank では false になると考えた誤りです。false true true 3 は、repeat(2) が元の文字列に 2 回足すと考えた誤りで、2 は全体の回数です。出典 https://docs.oracle.com/en/java/javase/1⁠7/docs/api/java.base/java/lang/String.html#isBlank()

問6次のコードを実行したときの結果として正しいものはどれですか。
public class Main {
    public static void main(String[] args) {
        String s = "yumesaku";
        int i = s.indexOf('u', 2);
        System.out.print(i + " ");
        System.out.print(s.charAt(s.length()));
    }
}

正解は「7 と出力したあと StringIndexOutOfBoundsException が発生する」です。indexOf('u', 2) は、添字 2 の位置から後ろに向かって u を探し、見つかった添字を返します。yumesaku の添字は y が 0、u が 1、m が 2 で、2 より後ろの u は末尾の 7 にあるので、i は 7 です。まず 7 と空白が出力されます。次の charAt に渡せる添字は 0 から length() - 1 までです。length() は 8 なので charAt(8) は範囲外で、API の説明どおり IndexOutOfBoundsException の仲間である StringIndexOutOfBoundsException が投げられます。7 u と出力されるという選択肢は、範囲外を読むと最後の文字が返ると考えた誤りです。1 で始まる選択肢は、探し始める位置の 2 を無視して先頭から探したときの値です。ArrayIndexOutOfBoundsException は配列の添字が範囲外のときの例外で、String の charAt では出ません。出典 https://docs.oracle.com/en/java/javase/1⁠7/docs/api/java.base/java/lang/String.html#charAt(int)

問7次のコードを実行したときの出力として正しいものはどれですか。
public class Main {
    public static void main(String[] args) {
        String s = "x";
        s += 1 + 2;
        s = s + 1 + 2;
        System.out.println(s);
    }
}

正解は「x3⁠1⁠2」です。s += 1 + 2; は複合代入で、s = s + (1 + 2) と同じ意味になります。右辺の 1 + 2 が先にまとめて計算されるので、int の 3 が s の後ろに連結され、s は x3 になります。次の s = s + 1 + 2; では、+ が左から順に評価されます。最初の s + 1 は片方が String なので文字列の連結になり x3⁠1、続く x3⁠1 + 2 も連結で x3⁠1⁠2 です。x1⁠2⁠1⁠2 は、1 行目も左から文字列として連結されると考えた誤りです。複合代入では右辺全体が先に計算されます。x3⁠3 は、2 行目の 1 + 2 が先に足し算されると考えた誤りです。括弧が無ければ左から順なので、文字列の後ろの数はそれぞれ文字列として連結されます。x3 は、2 行目の結果を s に代入していないと考えた誤りです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠5.html#jls-⁠1⁠5.2⁠6.2

問8次のコードを実行したときの出力として正しいものはどれですか。
public class Main {
    public static void main(String[] args) {
        String t = """
            ab
              c\
            d
            """;
        System.out.print(t.length() + "[" + t.replace(" ", "_").replace("\n", "|") + "]");
    }
}

正解は「8[ab|__cd|]」です。テキスト・ブロックの中身は、開始の三連のダブルクォートの直後の改行の次から始まるので、先頭の改行は文字列に入りません。次に、付随する空白 (全行に共通する字下げ) が取り除かれます。閉じる三連のダブルクォートの行も数に入り、どの行も少なくとも 1⁠2 文字下がっているので、各行の先頭の 1⁠2 文字が消えます。c の行だけは 2 文字多く下がっているので、空白 2 つが残ります。最後にエスケープが処理され、行末の \ は改行を打ち消してその行と次の行をつなぎます。そのため中身は ab、改行、空白 2 つと cd、改行で、長さは 8 です。閉じる三連のダブルクォートが次の行にあるので、最後の改行も入ります。7 で始まる選択肢は最後の改行を数えていません。9[|ab|__cd|] は先頭の改行が入ると考えた誤り、9[ab|__c|d|] は \ による行の連結を見落とした誤りです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠3.html#jls-⁠3.1⁠0.6

配列と ArrayList

問9次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    public static void main(String[] args) {
        int[] a, b[];
        int c[], d;
        a = new int[2];
        b = new int[2][];          // (A)
        d = 5;                     // (B)
        c = a;
        System.out.println(a.length + b.length + d + c.length);
    }
}

正解は「1⁠1 と出力される」です。配列の [] は、型の後ろにも変数名の後ろにも書けます。型の後ろに書いた [] は同じ宣言のすべての変数にかかり、変数名の後ろの [] はその変数だけにかかります。int[] a, b[]; では a が int[]、b が int[] にさらに [] が付いた int[][] です。int c[], d; では c が int[]、d はただの int です。そのため (A) で b に二次元配列を入れるのも、(B) で d に 5 を入れるのも正しく、コンパイルできます。a.length は 2、b.length は 2、d は 5、c は a と同じ配列なので c.length は 2 で、すべて int の足し算になり 1⁠1 と出力されます。(A) を誤りとするのは b も int[] だと考えた場合、(B) を誤りとするのは d も配列だと考えた場合です。2⁠2⁠5⁠2 は文字列の連結と考えた誤りです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠0.html#jls-⁠1⁠0.2

問10次のコードを実行したときの結果として正しいものはどれですか。
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> fixed = List.of("a", "b");
        List<String> copy = new ArrayList<>(fixed);
        copy.add("c");
        System.out.print(copy.size());
        fixed.add("c");   // (A)
    }
}

正解は「3 と出力したあと UnsupportedOperationException が発生する」です。List.of で作ったリストは変更できないリストで、add、remove、set などの変更のメソッドを呼ぶと、必ず UnsupportedOperationException が投げられます。ただし new ArrayList<>(fixed) は、fixed の要素を写した別の ArrayList を作ります。こちらは普通の可変のリストなので、copy.add("c") は成功し、copy.size() は 3 で、3 が出力されます。そのあと fixed.add("c") で例外が投げられます。2 と出力するという選択肢は、copy が fixed と同じリストを指すと考えた誤りです。コンストラクタに渡すと要素が写され、別のリストになります。3 と出力して正常に終わるという選択肢は、List.of のリストにも追加できると考えた誤りです。(A) の行でコンパイルエラーになるという選択肢も誤りで、型は List なので add の呼び出しはコンパイルでき、変更できないことは実行時に分かります。出典 https://docs.oracle.com/en/java/javase/1⁠7/docs/api/java.base/java/util/List.html#unmodifiable

問11次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    static int sum(int[] a) {
        int s = 0;
        for (int x : a) {
            s += x;
        }
        return s;
    }

    public static void main(String[] args) {
        int[] x = {1, 2, 3,};                      // (A)
        System.out.print(sum(x));
        System.out.print(sum(new int[]{4, 5}));   // (B)
    }
}

正解は「6⁠9 と出力される」です。配列の初期化子 {1, 2, 3,} の最後の要素の後ろのコンマは、言語仕様で許されていて無視されます。そのため (A) はコンパイルでき、x は 1、2、3 の 3 要素の配列です。(B) の new int[]{4, 5} は匿名配列と呼ばれる書き方で、変数に入れずにその場で配列を作り、メソッドの引数として渡せます。sum(x) は 6、sum(new int[]{4, 5}) は 9 で、print は改行しないので続けて 6⁠9 と出力されます。(A) を誤りとするのは、末尾のコンマが許されないと考えた場合です。(B) を誤りとするのは、初期化子付きの配列は宣言の中でしか作れないと考えた場合です。宣言の外で {4, 5} だけを書くとエラーですが、new int[] を付ければどこでも使えます。1⁠5 は 2 つの合計を足したもので、print が別々に出力することを見落としています。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠0.html#jls-⁠1⁠0.6

問12次のコードを実行したときの結果として正しいものはどれですか。
public class Main {
    public static void main(String[] args) {
        int[] a = new int[3];
        for (int i = 0; i <= a.length; i++) {
            a[i] = i * 2;
            System.out.print(a[i]);
        }
    }
}

正解は「0⁠2⁠4 と出力したあと ArrayIndexOutOfBoundsException が発生する」です。new int[3] で作った配列は要素が 3 つで、使える添字は 0、1、2 です。要素には int の既定値 0 が入っています。a.length は 3 です。for 文の条件が i <= a.length なので、i は 0、1、2、3 と進みます。i が 0 から 2 の間は a[i] に 0、2、4 を入れて出力し、0⁠2⁠4 と表示されます。i が 3 になると a[3] への代入で添字が範囲外になり、ArrayIndexOutOfBoundsException が投げられて止まります。それまでの出力は残ります。0⁠2⁠4 と出力されるという選択肢は、範囲外の添字が無視されると考えた誤りです。0⁠2⁠4⁠6 は、length が最後の添字を表すと考えた誤りです。何も出力されないという選択肢は、範囲外になるかどうかを実行の前に調べると考えた誤りで、配列の添字は使う時点で調べられます。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠0.html#jls-⁠1⁠0.4

問13次のコードを実行したときの出力として正しいものはどれですか。
public class Main {
    public static void main(String[] args) {
        int[][] g = {{1, 2, 3}, {4}, {}};
        int total = 0;
        for (int[] row : g) {
            total += row.length;
        }
        System.out.println(g.length + " " + total + " " + g[1][0]);
    }
}

正解は「3 4 4」です。Java の二次元配列は、配列を要素に持つ配列です。そのため行ごとに長さが違ってもかまいません (ジャグ配列)。g は {1, 2, 3}、{4}、{} の 3 つの配列を要素に持つので、g.length は 3 です。拡張 for 文で各行の length を足すと 3 + 1 + 0 で、total は 4 です。g[1] は {4} なので g[1][0] は 4 で、出力は 3 4 4 です。3 9 4 は、total が要素の値の合計 (1 から 4 の和) になると考えた誤りで、ここで足しているのは長さです。9 4 4 は、g.length が全体の要素数になると考えた誤りです。g.length は外側の配列の長さ、つまり行の数です。例外が発生するという選択肢は、空の行 {} があると止まると考えた誤りです。長さ 0 の配列も正しい配列で、length は 0 になります。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠0.html#jls-⁠1⁠0.6

問14次のコードを実行したときの出力として正しいものはどれですか。
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("c");
        list.add(1, "b");
        String old = list.set(2, "d");
        list.remove("a");
        System.out.println(old + list + list.size());
    }
}

正解は「c[b, d]2」です。add(1, "b") は添字 1 の位置に要素を差し込み、もとの要素を後ろにずらします。[a, c] は [a, b, c] になります。set(2, "d") は添字 2 の要素を置き換え、置き換える前の要素を戻り値で返します。リストは [a, b, d] になり、old は c です。remove("a") は引数が String なので remove(Object) が呼ばれ、最初に見つかった a が消えて [b, d] になります。size() は 2 です。出力は old と list と size を順に連結した c[b, d]2 です。d[b, d]2 は、set が新しい要素を返すと考えた誤りです。c[a, b, d]3 は、remove("a") が何もしないと考えた誤りです。c[b, c]2 は、add(1, "b") が要素を置き換えると考え、set の位置がずれていないとした誤りです。出典 https://docs.oracle.com/en/java/javase/1⁠7/docs/api/java.base/java/util/ArrayList.html

問15java.util.* を import しているとき、次の文のうちコンパイルエラーになるものはどれですか。

正解は「List<Number> a = new ArrayList<Integer>();」です。Integer は Number のサブクラスですが、ArrayList<Integer> は List<Number> のサブタイプではありません。型引数を持つ型どうしでは、型引数がちょうど同じでなければ代入できません。もし代入できると、a.add(1.5) で Integer のリストに Double が入ってしまうからです。var b = new ArrayList<>(); は、ダイヤモンド <> から推論する手がかりが無いので、型引数は Object になり、ArrayList<Object> としてコンパイルできます。List<Integer> c = new ArrayList<>(List.of(1)); は、左辺の型からダイヤモンドの中身が Integer と推論されます。Collection<String> d = new ArrayList<>(); も、ArrayList<String> は Collection<String> のサブタイプなので代入できます。型引数が同じなら、インタフェースの型の変数に入れられます。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠4.html#jls-⁠4.1⁠0.2

問16次のコードを実行したときの結果として正しいものはどれですか。
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<Integer> nums = new ArrayList<>(List.of(1, 2, 3, 4));
        for (Integer n : nums) {
            if (n == 2) {
                nums.remove(n);   // (A)
            }
        }
        System.out.println(nums);
    }
}

正解は「実行時に ConcurrentModificationException が発生する」です。拡張 for 文でリストを回すと、裏ではイテレータが使われます。ArrayList のイテレータはフェイル・ファストで、作られたあとにイテレータ自身の remove 以外でリストの構造が変わると、次の要素を取り出す時点で ConcurrentModificationException を投げます。n が 2 のとき nums.remove(n) で要素が消え、ループが次の要素を取りにいったところで例外が発生し、println には届きません。[1, 3, 4] は、回しながら消しても安全だと考えた誤りです。回しながら消すには、イテレータの remove か removeIf を使います。[1, 2, 4] は、remove(n) が添字 2 の要素を消すと考えた誤りです。n は Integer なので remove(Object) が選ばれ、値 2 を探して消します。(A) の行でコンパイルエラーになるという選択肢も誤りで、このコードは文法と型の上では正しく、問題は実行時に分かります。出典 https://docs.oracle.com/en/java/javase/1⁠7/docs/api/java.base/java/util/ArrayList.html#fail-⁠fast

ほかの分野

Java Silverの頁に、6分野の入口と試験の案内があります。

続きはゆめさくで

試験の知識を仕事につなげる一歩を、ここにまとめています。