Java | 1 日 90 分 × 7 日アプリ学習 初級編:Map を使うアプリ

Web APP Java
スポンサーリンク

2日目のゴール

2日目のテーマは
「Map を“ただの辞書”から、“更新・削除・一覧表示ができるデータ構造”として扱えるようになること」 です。

1日目であなたはすでに、

キーは「名前・ラベル・ID」
値は「本当に知りたい中身」
put(key, value) で登録
get(key) で取り出し
登録されていないキーは null

ここまではつかめています。

2日目ではここから一歩進んで、

同じキーに再度 put したらどうなるか
キーが存在するかどうかを調べる
キーと値を「全部なめる」
削除する

という、「Map をちゃんと“管理する”」感覚を身につけます。

題材は、小さな「ログイン回数カウンター」です。


同じ key に put すると“上書き”になる

1日目の復習を少しだけ深掘り

まずは、次のコードを見てください。

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, String> phoneBook = new HashMap<>();

        phoneBook.put("山田太郎", "090-1111-2222");
        phoneBook.put("山田太郎", "080-5555-6666");

        String yamada = phoneBook.get("山田太郎");
        System.out.println("山田太郎の電話番号: " + yamada);
    }
}
Java

出力は "080-5555-6666" になります。

理由はシンプルで、

同じキーに対する2回目の put が、
1回目の値を「上書き」するからです。

ここで押さえておきたいのは、

Map の中では、
「キーは一意(ユニーク)で、最後に登録した値が有効」
というルールがある、ということです。

この性質は、「設定」「最新状態」「カウンター」などを扱うときに、とても役立ちます。


Map を「カウンター」として使う

ログイン回数を数えるイメージ

次のような状況を考えてみます。

“alice” がログインした
“bob” がログインした
もう一度 “alice” がログインした

このとき、

“alice” → 2回
“bob” → 1回

という情報を持っておきたい。

ここで Map が使えます。

キー:ユーザー名(String)
値:ログイン回数(Integer)

という形にすればいい。

基本形のコード

import java.util.HashMap;
import java.util.Map;

public class LoginCounter {
    public static void main(String[] args) {
        Map<String, Integer> loginCount = new HashMap<>();

        countLogin(loginCount, "alice");
        countLogin(loginCount, "bob");
        countLogin(loginCount, "alice");

        System.out.println("alice のログイン回数: " + loginCount.get("alice"));
        System.out.println("bob のログイン回数: " + loginCount.get("bob"));
    }

    static void countLogin(Map<String, Integer> map, String userName) {
        Integer current = map.get(userName);
        if (current == null) {
            map.put(userName, 1);
        } else {
            map.put(userName, current + 1);
        }
    }
}
Java

ここでやっていることを、丁寧に言葉にします。

Map<String, Integer> は「ユーザー名 → 回数」の対応表
get(userName) で「今何回ログインしているか」を取り出す
まだ一度もログインしていないユーザーは null になる
null なら「初回ログイン」とみなして 1 を入れる
そうでなければ「今の回数 + 1」で上書きする

ここで重要なのは、

「Map は“現在の状態”を持つのに向いている」
という感覚です。

put は「追加」ではなく、「そのキーの“今の値”を更新する」イメージで捉えると、しっくりきます。


key が存在するかどうかを調べる

containsKey の意味

getnull が返ってきたとき、

「本当に登録されていない」のか
「値として null が入っている」のか

が区別できません。

値に null を入れない設計なら問題になりにくいですが、
Map にはそもそも「キーが存在するかどうか」を調べるメソッドがあります。

boolean exists = map.containsKey("alice");
Java

containsKey(key) は、

そのキーが登録されていれば true
登録されていなければ false

を返します。

get と containsKey を組み合わせる

さっきのカウンターを、containsKey を使って書き直してみます。

static void countLogin(Map<String, Integer> map, String userName) {
    if (!map.containsKey(userName)) {
        map.put(userName, 1);
    } else {
        int current = map.get(userName);
        map.put(userName, current + 1);
    }
}
Java

やっていることは同じですが、

「キーが存在するかどうか」を明示的にチェックしている

という点で、意図がよりはっきりします。


Map の中身を「全部なめる」

keySet() でキーの一覧を取る

Map は、List と違って「0番目」「1番目」という概念がありません。
その代わりに、

「登録されているキーの一覧」
「登録されている値の一覧」
「キーと値のペアの一覧」

を取り出すメソッドが用意されています。

まずはキーの一覧。

for (String user : loginCount.keySet()) {
    Integer count = loginCount.get(user);
    System.out.println(user + " : " + count);
}
Java

keySet() は、

「Map に登録されている全てのキーの集合」

を返します。

それを拡張 for 文で回して、
get(key) で値を取り出す、というパターンです。

例:ログイン回数を全部表示する

さっきの LoginCounter に、一覧表示を足してみます。

static void showAll(Map<String, Integer> map) {
    System.out.println("=== ログイン回数一覧 ===");
    for (String user : map.keySet()) {
        Integer count = map.get(user);
        System.out.println(user + " : " + count);
    }
}
Java

Main からはこう呼べます。

showAll(loginCount);
Java

ここで感じてほしいのは、

List のときは「インデックスで回す」
Map のときは「キーの一覧で回す」

という違いです。


key / value のペアを直接扱う

entrySet() のイメージ

もう一歩進んで、
「キーと値のペア」を直接扱う方法もあります。

for (Map.Entry<String, Integer> entry : loginCount.entrySet()) {
    String user = entry.getKey();
    Integer count = entry.getValue();
    System.out.println(user + " : " + count);
}
Java

entrySet() は、

「キーと値のペア(Entry)の集合」

を返します。

Entry は、

getKey() でキー
getValue() で値

を取り出せる、小さなオブジェクトです。

今日の段階では、

「キー一覧で回す方法」と
「Entry で回す方法」がある

と知っておけば十分です。


Map から削除する

remove(key) の基本

Map から「このキーのデータはいらない」と消したいときは、
remove を使います。

loginCount.remove("bob");
Java

これで、

“bob” → 1

という対応関係が、Map から消えます。

remove したあとに get("bob") すると、
null が返ってきます。

例:一定回数以下のユーザーを消す

少しだけ応用してみます。

「ログイン回数が 1 回だけのユーザーは消す」
という処理を書いてみましょう。

static void removeLowUsers(Map<String, Integer> map) {
    // 削除を伴うので、まずキーをコピーする
    java.util.List<String> keys = new java.util.ArrayList<>(map.keySet());

    for (String user : keys) {
        int count = map.get(user);
        if (count <= 1) {
            map.remove(user);
        }
    }
}
Java

ここで大事なのは、

Map を回しながら直接 remove すると危険なので、
一度キーの一覧を別のリストにコピーしてから回している

という点です。

「削除を伴うループは、対象をコピーしてから回す」
というのは、Map でも List でも役立つ考え方です。


2日目のミニアプリ完成形

LoginCounter 全体

import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;

public class LoginCounter {
    public static void main(String[] args) {
        Map<String, Integer> loginCount = new HashMap<>();

        countLogin(loginCount, "alice");
        countLogin(loginCount, "bob");
        countLogin(loginCount, "alice");
        countLogin(loginCount, "charlie");

        showAll(loginCount);

        System.out.println("1回しかログインしていないユーザーを削除します。");
        removeLowUsers(loginCount);

        showAll(loginCount);
    }

    static void countLogin(Map<String, Integer> map, String userName) {
        Integer current = map.get(userName);
        if (current == null) {
            map.put(userName, 1);
        } else {
            map.put(userName, current + 1);
        }
    }

    static void showAll(Map<String, Integer> map) {
        System.out.println("=== ログイン回数一覧 ===");
        for (String user : map.keySet()) {
            Integer count = map.get(user);
            System.out.println(user + " : " + count);
        }
    }

    static void removeLowUsers(Map<String, Integer> map) {
        ArrayList<String> keys = new ArrayList<>(map.keySet());
        for (String user : keys) {
            int count = map.get(user);
            if (count <= 1) {
                map.remove(user);
            }
        }
    }
}
Java

このコードを眺めてみてください。

ユーザー名 → ログイン回数
という「key / value」の関係が、
Map の中にきれいに収まっています。

put で「今の状態」を更新し、
get で取り出し、
keySet で一覧を回し、
remove でいらないものを消す。

これが、2日目で押さえておきたい「Map の基本操作フルセット」です。


2日目で絶対に押さえてほしい本質

今日いちばん大事なのは、
「Map は“キーから今の状態を一発で引ける表”だ」
という感覚を持てたかどうかです。

同じキーに put すると「上書き」になる
get で取り出し、null なら「登録されていない」と判断する
containsKey で「キーの存在」を明示的にチェックできる
keySetentrySet で「全部なめる」ことができる
remove で「このキーのデータはいらない」と消せる

このあたりが、自然に口から説明できるようになっていれば、
2日目としてはかなりいい仕上がりです。

次のステップでは、

値にオブジェクトを入れる(Map<String, User>
Map と List を組み合わせる
「設定」「キャッシュ」「インデックス」としての Map

といった、より“アプリっぽい”使い方に踏み込んでいきます。

タイトルとURLをコピーしました