解説 · セクション 3

List/Map で結果を持ち回る

DAO の戻り値 List、リクエストパラメータの Map をアナロジーで腑に落とします。

読み終わったら問題に挑戦できます → 演習に進む

身近に置き換えると

レストランの業務に例えてみましょう。データベースからデータを取り出して画面に運ぶのは「厨房から出来上がった料理をお盆に乗せて運ぶ」ようなものです。この「お盆」の役割を果たすのが List です。お盆には決まった料理しか乗せないため、何を乗せるかを明確にします。

一方、ユーザーがブラウザから送ってくる入力データは「注文伝票」です。「ハンバーガー1つ、トッピングはチーズとトマト」のように、1つの項目(キー)に対して複数の内容(値)が書かれることがあるため、伝票は Map という形式で受け取ります。

3 行でまとめると

  • List<Employee> は、データベースから取り出した複数のデータをひとまとめにして運ぶ「専用のお盆」です。
  • while (rs.next()) でデータを1件ずつ DTO に詰め、list.add() でお盆に乗せていきます。
  • ブラウザからの入力データは Map<String, String[]> で受け取ります。同じ名前で複数の値が送られてくることがあるため、値は配列になります。

図で見ると

flowchart LR
    subgraph S1["DAO で List を返す"]
        DB[(データベース)] -->|"1件ずつ取得"| RS[while rs.next]
        RS -->|"DTO に詰める"| DTO[Employee]
        DTO -->|"list.add"| L["List<Employee>"]
    end

    subgraph S2["Servlet が Map で受け取る"]
        Browser[ブラウザ] -->|"送信"| SV[Servlet]
        SV -->|"getParameterMap"| M["Map<String, String 配列>"]
    end

データベースから取得した結果は List に詰めてアプリケーション内で持ち回り、ブラウザからのリクエストは Map で受け取って処理をします。

順を追って理解する

1. 専用のお盆を用意する (List とジェネリクス)

複数のデータを運ぶために List を使いますが、「何を入れるリストか」を明確にするために ジェネリクス <Employee> を使います。インスタンスを作る際は new ArrayList<>() のように書きます。右側のカッコ内の型を省略できるこの書き方を ダイヤモンド演算子 と呼びます。

2. お盆にデータを乗せる (while と add)

データベースからデータを取り出すときは、while (rs.next()) を使って1行ずつ処理します。取り出したデータから Employee オブジェクト(DTO)を作り、list.add(e) でリストに追加していきます。これで複数件のデータが1つのお盆にまとまり、メソッドの戻り値として安全に返すことができます。

3. お盆からデータを取り出す (拡張 for 文)

まとめたリストの中身を1つずつ確認するには 拡張 for 文 が便利です。for (Employee e : list) のように書きます。リストには Employee しか入っていないことがジェネリクスによって保証されているため、仮変数の型も迷わず Employee になります。

4. 注文伝票を受け取る (Map と getParameterMap)

ブラウザから送信されたデータ(リクエストパラメータ)は、Servlet で getParameterMap() を使って受け取ります。この戻り値の型は Map<String, String[]> です。なぜ値が String ではなく String[](配列)なのでしょうか? それは、チェックボックスのように「1つの項目(例:hobby)に対して複数の値(例:読書、映画)」が同時に送られてくる可能性があるからです。

応用ではこう書く

// --- DAOクラスのメソッド ---
public List<Employee> findAll() {
    // 1. Employee専用のリスト(お盆)を用意する
    List<Employee> list = new ArrayList<>();
    
    // (データベース接続とSQL実行の処理は省略)
    
    // 2. 1行ずつ取り出してDTOを作り、リストに詰める
    while (rs.next()) {
        Employee e = new Employee();
        e.setId(rs.getInt("id"));
        e.setName(rs.getString("name"));
        list.add(e);
    }
    return list; // まとめたリストを戻り値として返す
}

// --- Servletの処理 ---
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
    // 4. 送信されたデータをMapとして受け取る
    Map<String, String[]> paramMap = request.getParameterMap();
    
    // "hobby" というキーに対して、複数の値が配列で入っている
    String[] hobbies = paramMap.get("hobby");
}

// --- 別のクラスでの表示処理 ---
public void printEmployees(List<Employee> list) {
    // 3. 拡張for文でリストから1つずつ取り出す
    for (Employee e : list) {
        System.out.println(e.getName());
    }
}

DAO で作られた List は Servlet を経由して JSP に渡され、画面表示に使われます。データの受け渡しには必ずこれらのコレクションが活躍します。

よくある誤解

誤解: Map の値は常に1つの文字列だと思って String で受け取ろうとする

正しくは: ブラウザからの入力データを受け取る getParameterMap() の戻り値は Map<String, String[]> です。テキストボックスの入力が1つだけでも、仕様上必ず「配列」として格納されています。単一の値を取り出したい場合は request.getParameter("name") を使うか、配列の1つ目 配列[0] にアクセスする必要があります。

誤解: 拡張 for 文の仮変数の型を Object にしてしまう

正しくは: List<Employee> を拡張 for 文で回す場合、取り出される要素は必ず Employee 型になります。for (Object e : list) のように曖昧な型にする必要はありません。ジェネリクス <Employee> のおかげで、安全かつ明確に型を指定してメソッドを呼び出すことができます。

つまずいたら

このトピックで詰まったら基礎復習へ: 章 5 コレクション

準備できたら問題に挑戦 → 演習に進む (5 問)