7日目のゴール
7日目のテーマは
「Map を“アプリ全体の中でどこに置くか”を意識して設計できるようになること」 です。
ここまで6日間で、あなたはすでにかなりのことをやっています。
key は「名前・ID・ラベル」
value は「本当に知りたい中身」Map<String, Integer> でカウンターやスコアMap<String, User> で「ID → ユーザー」Map<String, List<何か>> でグループ分け
マスタ(List)+インデックス(Map)という構造
7日目では、これらをバラバラのテクニックとしてではなく、
ひとつの「設計の型」として頭に定着させる ことをゴールにします。
題材は、小さな「EC風ミニアプリ」です。
ユーザーがいる
商品がある
注文がある
この3つを、List と Map を組み合わせて扱っていきます。
もう一度だけ整理する:Map の本質
key / value を“言葉”で言い切る
Map を一言で言うなら、こうです。
「キー(key)から値(value)を一発で引くための表」
ここでのポイントは三つです。
キーは「何を知りたいか」を表すラベル
値は「そのときの答え」
キーは一意(同じキーは1つだけ)、値は重複してもよい
そして、使い方のイメージはこうです。
put(key, value) は「このキーの答えは今これです、と登録する」get(key) は「このキーの今の答えを教えて」と聞くremove(key) は「このキーの答えはもういらない」と消す
ここに、
「なければデフォルト」
「マスタ+インデックス」
「グループ分け」
といった考え方を乗せていくと、
Map は一気に“アプリの中枢”になります。
今日の世界観:ユーザー・商品・注文
3つの登場人物を言葉で決める
まずは、コードを書く前に世界観を言葉で固めます。
ユーザー
商品
注文
ユーザーは「ユーザーID」「名前」「メールアドレス」を持つ
商品は「商品ID」「名前」「価格」を持つ
注文は「注文ID」「どのユーザーが」「どの商品を」「いくつ買ったか」を持つ
そして、アプリとしてやりたいことはこうです。
ユーザーを登録する
商品を登録する
ユーザーIDと商品IDを指定して注文を作る
ユーザーIDから、そのユーザーの注文一覧を見たい
注文IDから、注文の詳細を一発で見たい
ここで、Map の出番がはっきり見えてきます。
ユーザーID → User
商品ID → Product
注文ID → Order
ユーザーID → そのユーザーの注文リスト
という key/value の世界です。
User / Product / Order をクラスで表現する
User クラス
public class User {
String id;
String name;
String email;
User(String id, String name, String email) {
if (id == null || id.isEmpty()) {
throw new IllegalArgumentException("ユーザーIDは必須です。");
}
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("名前は必須です。");
}
if (email == null || email.isEmpty()) {
throw new IllegalArgumentException("メールアドレスは必須です。");
}
this.id = id;
this.name = name;
this.email = email;
}
void show() {
System.out.println("User[ID=" + id + ", 名前=" + name + ", メール=" + email + "]");
}
}
JavaProduct クラス
public class Product {
String id;
String name;
int price;
Product(String id, String name, int price) {
if (id == null || id.isEmpty()) {
throw new IllegalArgumentException("商品IDは必須です。");
}
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("商品名は必須です。");
}
if (price < 0) {
throw new IllegalArgumentException("価格は 0 以上で指定してください。");
}
this.id = id;
this.name = name;
this.price = price;
}
void show() {
System.out.println("Product[ID=" + id + ", 名前=" + name + ", 価格=" + price + "円]");
}
}
JavaOrder クラス
public class Order {
String id;
User user;
Product product;
int quantity;
Order(String id, User user, Product product, int quantity) {
if (id == null || id.isEmpty()) {
throw new IllegalArgumentException("注文IDは必須です。");
}
if (user == null) {
throw new IllegalArgumentException("ユーザーは必須です。");
}
if (product == null) {
throw new IllegalArgumentException("商品は必須です。");
}
if (quantity <= 0) {
throw new IllegalArgumentException("数量は 1 以上で指定してください。");
}
this.id = id;
this.user = user;
this.product = product;
this.quantity = quantity;
}
int totalPrice() {
return product.price * quantity;
}
void show() {
System.out.println("Order[ID=" + id + ", ユーザー=" + user.name +
", 商品=" + product.name + ", 数量=" + quantity +
", 合計=" + totalPrice() + "円]");
}
}
Javaここまでで、「1件分の世界」は整いました。
次は、これらを Map と List でどうつなぐか、です。
Map をどこに置くかを決める
ECApp クラスの役割
アプリ全体を管理するクラスを作ります。
ユーザーのマスタ(List)
商品のマスタ(List)
注文のマスタ(List)
ユーザーID → User の Map
商品ID → Product の Map
注文ID → Order の Map
ユーザーID → そのユーザーの注文リスト の Map
これをコードにすると、こうなります。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class ECApp {
ArrayList<User> users;
ArrayList<Product> products;
ArrayList<Order> orders;
Map<String, User> userById;
Map<String, Product> productById;
Map<String, Order> orderById;
Map<String, ArrayList<Order>> ordersByUserId;
ECApp() {
users = new ArrayList<>();
products = new ArrayList<>();
orders = new ArrayList<>();
userById = new HashMap<>();
productById = new HashMap<>();
orderById = new HashMap<>();
ordersByUserId = new HashMap<>();
}
}
Javaここでの超重要ポイントは、
同じ User / Product / Order を、
List と複数の Map から「共有して見ている」
という構造です。
List は「全部を順番に眺めるマスタ」
Map は「特定のキーから一発で引くためのインデックス」
という役割分担が、ここで一気にそろいます。
ユーザーと商品を登録する
ユーザー登録
void addUser(User user) {
if (user == null) {
System.out.println("null のユーザーは登録できません。");
return;
}
if (userById.containsKey(user.id)) {
System.out.println("そのユーザーIDはすでに使われています: " + user.id);
return;
}
users.add(user);
userById.put(user.id, user);
}
Java商品登録
void addProduct(Product product) {
if (product == null) {
System.out.println("null の商品は登録できません。");
return;
}
if (productById.containsKey(product.id)) {
System.out.println("その商品IDはすでに使われています: " + product.id);
return;
}
products.add(product);
productById.put(product.id, product);
}
Javaここでやっていることは、6日目の「マスタ+インデックス」と同じです。
マスタの List に追加する
ID インデックスの Map にも登録する
この「二箇所更新」が、Map を設計に組み込むときの基本パターンです。
注文を作るときに Map をフル活用する
createOrder の流れ
注文を作るときに必要なのは、
ユーザーIDから User を引く
商品IDから Product を引く
注文IDが重複していないか確認する
Order を作って、マスタとインデックスを全部更新する
です。
コードにすると、こうなります。
Order createOrder(String orderId, String userId, String productId, int quantity) {
if (orderById.containsKey(orderId)) {
System.out.println("その注文IDはすでに使われています: " + orderId);
return null;
}
User user = userById.get(userId);
if (user == null) {
System.out.println("そのユーザーIDは存在しません: " + userId);
return null;
}
Product product = productById.get(productId);
if (product == null) {
System.out.println("その商品IDは存在しません: " + productId);
return null;
}
Order order;
try {
order = new Order(orderId, user, product, quantity);
} catch (IllegalArgumentException e) {
System.out.println("注文の作成に失敗しました: " + e.getMessage());
return null;
}
orders.add(order);
orderById.put(order.id, order);
ArrayList<Order> list = ordersByUserId.get(user.id);
if (list == null) {
list = new ArrayList<>();
ordersByUserId.put(user.id, list);
}
list.add(order);
return order;
}
Javaここで Map がどう使われているか、整理してみます。
ユーザーID → User を引くために userById.get(userId)
商品ID → Product を引くために productById.get(productId)
注文ID → Order の重複チェックに orderById.containsKey(orderId)
ユーザーID → 注文リスト のために ordersByUserId.get(user.id)
つまり、Map が「検索の近道」として、
あらゆるところで使われています。
注文IDから詳細を一発で見る
showOrderById メソッド
void showOrderById(String orderId) {
Order order = orderById.get(orderId);
if (order == null) {
System.out.println("その注文IDの注文は存在しません: " + orderId);
return;
}
order.show();
}
JavaList だけで同じことをやろうとすると、
全注文をループして ID を比較する必要があります。
Map をインデックスとして持っていれば、get(orderId) 1発です。
ここで改めて、
「Map は“検索のインデックス”」
という感覚を、しっかり自分の言葉にしておいてください。
ユーザーごとの注文一覧を表示する
showOrdersByUser メソッド
void showOrdersByUser(String userId) {
User user = userById.get(userId);
if (user == null) {
System.out.println("そのユーザーIDは存在しません: " + userId);
return;
}
System.out.println("=== ユーザー " + user.name + " の注文一覧 ===");
ArrayList<Order> list = ordersByUserId.get(userId);
if (list == null || list.isEmpty()) {
System.out.println("このユーザーの注文はありません。");
return;
}
for (Order o : list) {
o.show();
}
}
Javaここでは、
ユーザーID → User を引く Map
ユーザーID → 注文リスト を引く Map
の両方を使っています。
ユーザーの存在確認と表示には userById
注文一覧には ordersByUserId
という役割分担です。
Main から見た「EC風ミニアプリ」
全体の動き
public class Main {
public static void main(String[] args) {
ECApp app = new ECApp();
app.addUser(new User("u001", "山田太郎", "yamada@example.com"));
app.addUser(new User("u002", "佐藤花子", "sato@example.com"));
app.addProduct(new Product("p001", "Java入門", 3000));
app.addProduct(new Product("p002", "ワイヤレスマウス", 1500));
app.createOrder("o001", "u001", "p001", 2);
app.createOrder("o002", "u001", "p002", 1);
app.createOrder("o003", "u002", "p002", 3);
System.out.println();
app.showOrderById("o002");
System.out.println();
app.showOrdersByUser("u001");
System.out.println();
app.showOrdersByUser("u002");
System.out.println();
app.showOrderById("o999"); // 存在しない注文
}
}
Javaこの Main を眺めてみてください。
ユーザーを登録する
商品を登録する
注文を作る
注文IDから詳細を見る
ユーザーごとの注文一覧を見る
ここには、Map という単語は一切出てきません。
見えているのは、
ユーザー
商品
注文
ID 検索
ユーザーごとの履歴
という「アプリの世界」だけです。
裏側では、
Map<String, User>Map<String, Product>Map<String, Order>Map<String, List<Order>>
という key/value の世界が、
アプリの「検索」「紐づけ」「グループ分け」を支えています。
7日目で本当に持ち帰ってほしいこと
最後に、Map についての“芯”を一つにまとめます。
Map は、
「キーから値を一発で引くための表」であり、
アプリの中で“今の状態・検索の近道・グループ分け”を支える中枢
です。
キーは「何を知りたいか」
値は「そのときの答え」
Map<String, User> なら「ユーザーID → ユーザー」Map<String, Product> なら「商品ID → 商品」Map<String, List<Order>> なら「ユーザーID → 注文のリスト」
そして、List と組み合わせるときの型はこうです。
マスタ(全部入りの List)
インデックス(Map)
この二階建て構造を、
自分で設計できるようになっていたら、
あなたはもう「Map を知っている人」ではなく、
「Map を前提にアプリの世界を設計できる人」 です。
もしこのあと、自分で小さなアプリを考えるなら、
こう自分に問いかけてみてください。
「この世界で“ID で引きたいもの”は何だろう?」
「この世界で“グループ分けしたいもの”は何だろう?」
その答えが見えたところに、
必ず 1 つ以上の Map が座る場所が生まれます。


