コンテンツにスキップ

9-3. 演習問題


1-1. 次のコードの出力はどれか。

List<String> list = new ArrayList<>(List.of("c", "a", "b"));
list.add("a");
System.out.println(list.size());
System.out.println(list.contains("a"));
  • A. 3false
  • B. 4true
  • C. 3true
  • D. コンパイルエラー
解答と解説

正解:B

List は重複を許すため、"a" を追加しても既存の "a" は除去されない。size()4(c, a, b, a)になる。contains("a")true

Set なら重複を許さないため size()3 になる。コレクションを選ぶ際は重複を許すかどうかが重要な判断基準だ。


1-2. HashMap の特性として正しいものはどれか。

  • A. キーの順序が挿入順に保証される
  • B. キーが重複した場合、古い値が保持される
  • C. null をキーや値として使用できる
  • D. スレッドセーフである
解答と解説

正解:C

  • A:HashMap の順序は保証されない。挿入順を保持したいなら LinkedHashMap を使う
  • B:キーが重複した場合、新しい値で上書きされる。put("key", "old")put("key", "new") → 値は "new"
  • C:正しいHashMapnull キーを1つだけ、null 値は複数持てる(Hashtable と異なる点)
  • D:HashMap はスレッドセーフではない。マルチスレッド環境では ConcurrentHashMap を使う

1-3. 次のStream APIのコードで result に格納される値はどれか。

List<Integer> nums = List.of(1, 2, 3, 4, 5);
int result = nums.stream()
.filter(n -> n % 2 != 0)
.mapToInt(Integer::intValue)
.sum();
  • A. 6(偶数の合計)
  • B. 9(奇数の合計)
  • C. 15(全合計)
  • D. 3(奇数の個数)
解答と解説

正解:B

処理を順に追う:

  1. filter(n -> n % 2 != 0):奇数のみ → [1, 3, 5]
  2. mapToInt(Integer::intValue)IntStream(プリミティブのストリーム)に変換
  3. .sum():合計 → 1 + 3 + 5 = 9

mapToIntMap<Integer> ではなく IntStream を返すため、sum()average()min()max() などのプリミティブ向け終端操作が使える。


1-4. ラムダ式 (a, b) -> a.compareTo(b) と同じ意味のメソッド参照はどれか。

  • A. String::compareTo
  • B. Comparator::compare
  • C. String::compareToIgnoreCase
  • D. Comparator.naturalOrder()
解答と解説

正解:A

(a, b) -> a.compareTo(b)StringcompareTo メソッドを a(第1引数)のインスタンスに対して呼んでいる。これはインスタンスメソッド参照で String::compareTo と書ける。

実務でのソート:

List<String> list = new ArrayList<>(List.of("banana", "apple", "cherry"));
list.sort((a, b) -> a.compareTo(b)); // ラムダ式
list.sort(String::compareTo); // メソッド参照(同じ意味)
list.sort(Comparator.naturalOrder()); // 標準的な自然順序
list.sort(Comparator.reverseOrder()); // 逆順

2. 次のコードの( )に入るコードを答えよ。

List<String> names = List.of("Alice", "Bob", "Charlie", "Diana");
// (1) 名前の長さが5以上のものだけを取り出す
List<String> longNames = names.(1)()
.filter(s -> s.(2)() >= 5)
.collect(Collectors.toList());
// ["Alice", "Charlie", "Diana"]
// (2) 全ての名前を大文字に変換してリスト化
List<String> upper = names.stream()
.map(String::3)
.collect(Collectors.toList());
// (3) 名前の文字数の合計
int total = names.stream()
.(4ToInt(String::length)
.sum();
解答欄
解答と解説
  1. stream
  2. length
  3. toUpperCase
  4. map

解説

(1) コレクションをStreamに変換するには .stream() を呼ぶ。

(2) String.length() で文字数を取得する。

(3) String::toUpperCase はメソッド参照。s -> s.toUpperCase() と同じ意味。

(4) mapToIntStream<String> から IntStream に変換し、.sum() で合計できる。map だと Stream<Integer> になるため .sum() が使えない。

3-1. for ループと Stream API を使った書き方の違いを、可読性・副作用・再利用性の観点で比較せよ。

解答欄
解答と解説

for ループ(命令型スタイル)

List<Integer> result = new ArrayList<>();
for (Integer n : numbers) {
if (n % 2 == 0) {
result.add(n * n);
}
}

Stream API(宣言型スタイル)

List<Integer> result = numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.collect(Collectors.toList());
観点for ループStream API
可読性処理の手順が細かく書かれる「何をするか」が簡潔に表現される
副作用外部の変数(result)を変更する元のコレクションを変更しない(非破壊的)
再利用性ループを使い捨てで書くことが多い.filter() の条件等を変数にして再利用しやすい
デバッグステップ実行しやすい中間状態の確認は .peek() を使う

実務では Stream API が好まれる場面が多いが、複雑なロジックや早期リターンが必要な場合は for ループの方がシンプルになることもある。


3-2. 次のコードを Stream API を使って書き直せ。

List<String> result = new ArrayList<>();
for (Book book : books) {
if (book.genre().equals("tech") && book.price() >= 3000) {
result.add(book.title().toUpperCase());
}
}
Collections.sort(result);

Bookrecord Book(String title, String author, int price, String genre) {} とする。

解答欄
解答と解説
List<String> result = books.stream()
.filter(b -> b.genre().equals("tech") && b.price() >= 3000)
.map(b -> b.title().toUpperCase())
.sorted()
.collect(Collectors.toList());

ポイント:

  • filter の条件は && で複数組み合わせられる
  • map で変換と同時に toUpperCase() を適用
  • sorted() でソートをStreamの中に組み込める(Collections.sort() 不要)
  • 元の books リストは変更されない(非破壊的)

下の Java WASM プレイグラウンドで List・Stream API・Collectors.partitioningBy() を使った集計を実装せよ。

  1. 平均点が80以上の生徒名一覧をソート付きで出力する
  2. 全生徒の合計点平均を mapToIntaverage() で求める
  3. 数学最高点の生徒を max で求める
  4. partitioningBy() で「80以上 / 80未満」に分けた Map を作って出力する
Java WASM Playground
ブラウザ内で Java をコンパイルして実行します / 編集内容は自動保存されます / Ctrl+Enter でも実行できます
使い方: 1ファイルならそのまま編集できます。複数ファイルに分けたい場合は // File: Main.java のような行で区切ってください。
出力
「実行」を押すと、ここにコンパイル結果と実行結果が表示されます。
  • mapToInt を使うと、なぜ average()sum() が自然に書けるか
  • sorted()Comparator.comparingInt() の役割の違い
  • partitioningBy()Map<Boolean, List<T>> を返す意味