Java Tips | コレクション:Mapキー抽出

Java Java
スポンサーリンク

「Mapキー抽出」は“辞書の見出しだけを取り出す”イメージ

Map<K, V> は「キーと値のペアの集まり」です。
「商品ID → 商品名」「ユーザーID → ユーザー情報」「コード → マスタ値」など、業務で頻出します。

その中で「キーだけ欲しい」「値は要らない」という場面がかなり多いです。
たとえば「登録済みユーザーID一覧が欲しい」「存在チェック用にキーだけを別の処理に渡したい」など。

この「Map からキーだけを安全に・分かりやすく取り出す」のが、今回のテーマです。


基本形:keySet でキーの“ビュー”を取り出す

Map#keySet の挙動を正しく理解する

一番基本は Map#keySet() です。

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

public class KeySetBasic {

    public static void main(String[] args) {
        Map<String, String> userMap = new HashMap<>();
        userMap.put("u001", "山田");
        userMap.put("u002", "佐藤");
        userMap.put("u003", "鈴木");

        Set<String> keys = userMap.keySet();

        System.out.println(keys); // [u001, u002, u003] のようなイメージ
    }
}
Java

ここで重要なのは、「keySet() が“ビュー”である」という点です。
ビューというのは、「元の Map と中身を共有している“見え方”だけのオブジェクト」という意味です。

つまり、userMap にキーを追加・削除すると、keys にも反映されます。
逆に、keys.remove("u001") のようにキー集合から削除すると、元の userMap からもそのエントリが消えます。

「キー一覧を眺めるだけ」ならビューで問題ありませんが、
「独立したコレクションとして扱いたい」場合はコピーが必要になります。


List としてキーを扱いたいときのユーティリティ

Set ではなく List が欲しい場面は多い

業務では、「キーを順番付きの List として扱いたい」ことがよくあります。
たとえば「キー一覧をソートしたい」「先頭 N 件だけ取りたい」など。

その場合は、keySet() から List を作るユーティリティを用意しておくと便利です。

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public final class MapKeys {

    private MapKeys() {}

    public static <K, V> List<K> toList(Map<K, V> map) {
        if (map == null || map.isEmpty()) {
            return List.of();
        }
        return new ArrayList<>(map.keySet());
    }
}
Java

使い方はこうです。

Map<String, String> userMap = Map.of(
        "u001", "山田",
        "u002", "佐藤",
        "u003", "鈴木"
);

List<String> userIds = MapKeys.toList(userMap);

System.out.println(userIds); // [u001, u002, u003] など
Java

ここでの重要ポイントは二つです。

一つ目は、「new ArrayList<>(map.keySet()) で“独立した List”を作っている」ことです。
これにより、元の Map を変更しても List 側は変わりませんし、List を変更しても Map には影響しません。

二つ目は、「null や空 Map の扱いをユーティリティ側で決めている」ことです。
呼び出し側は「キー一覧が欲しい」とだけ考えればよく、毎回 null チェックを書く必要がなくなります。


ソートされたキー一覧が欲しいとき

キーを自然順・任意順で並べ替える

「キー一覧を画面に表示する」「ログに出す」などの場面では、
ソートされた順番で扱いたいことが多いです。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public final class MapKeys {

    private MapKeys() {}

    public static <K extends Comparable<? super K>, V> List<K> sorted(Map<K, V> map) {
        if (map == null || map.isEmpty()) {
            return List.of();
        }
        List<K> list = new ArrayList<>(map.keySet());
        Collections.sort(list); // 自然順(Comparable)でソート
        return list;
    }
}
Java

使い方はこうです。

Map<Integer, String> codeMap = Map.of(
        200, "OK",
        404, "Not Found",
        500, "Server Error"
);

List<Integer> sortedCodes = MapKeys.sorted(codeMap);
// [200, 404, 500]
Java

ここでの重要ポイントは、
「キーが Comparable を実装している前提で自然順ソートしている」ことです。

もし独自の順番で並べたいなら、Comparator を受け取るオーバーロードを用意してもよいです。

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

public static <K, V> List<K> sorted(
        Map<K, V> map,
        Comparator<? super K> comparator
) {
    if (map == null || map.isEmpty()) {
        return List.of();
    }
    List<K> list = new ArrayList<>(map.keySet());
    list.sort(comparator);
    return list;
}
Java

条件付きでキーを抽出する(フィルタ+キー抽出)

「値に基づいてキーを取りたい」パターン

業務では、「値が◯◯なもののキーだけ欲しい」というケースがよくあります。
たとえば「ステータスが ACTIVE のユーザーID一覧」など。

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public final class MapKeys {

    private MapKeys() {}

    public static <K, V> List<K> keysWhereValue(
            Map<K, V> map,
            java.util.function.Predicate<? super V> valueCondition
    ) {
        if (map == null || map.isEmpty()) {
            return List.of();
        }
        return map.entrySet().stream()
                  .filter(e -> valueCondition.test(e.getValue()))
                  .map(Map.Entry::getKey)
                  .collect(Collectors.toList());
    }
}
Java

使い方の例です。

enum Status { ACTIVE, INACTIVE }

Map<String, Status> userStatusMap = Map.of(
        "u001", Status.ACTIVE,
        "u002", Status.INACTIVE,
        "u003", Status.ACTIVE
);

List<String> activeUserIds =
        MapKeys.keysWhereValue(userStatusMap, s -> s == Status.ACTIVE);

System.out.println(activeUserIds); // 例: [u001, u003]
Java

ここで深掘りしたい重要ポイントは三つです。

一つ目は、「entrySet().stream() から filtermap(getKey) という流れで、“値で絞ってキーを取り出す”処理を素直に書いている」ことです。

二つ目は、「Predicate<? super V> valueCondition が“どんな値のときにキーを採用するか”を表している」ことです。
v -> v == Status.ACTIVE のように、業務ルールをラムダで渡せます。

三つ目は、「キー抽出とフィルタをユーティリティに閉じ込めることで、呼び出し側のコードが“何をしたいか”だけに集中できる」ことです。
MapKeys.keysWhereValue(userStatusMap, 条件) と書けば、「値の条件でキーを抽出しているんだな」と一目で分かります。


Stream を使ったキー抽出の基本パターン

「Map → Stream → キー一覧」の流れに慣れる

Stream を使うと、キー抽出は次のような基本形になります。

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

Map<String, Integer> scores = Map.of(
        "山田", 80,
        "佐藤", 90,
        "鈴木", 70
);

// キーだけの List
List<String> names =
        scores.keySet().stream()
              .collect(Collectors.toList());

// 値が 80 以上のキーだけ
List<String> highScorers =
        scores.entrySet().stream()
              .filter(e -> e.getValue() >= 80)
              .map(Map.Entry::getKey)
              .collect(Collectors.toList());
Java

ここでの重要ポイントは二つです。

一つ目は、「キーだけ欲しいなら keySet().stream()、値条件で絞りたいなら entrySet().stream()」という使い分けです。

二つ目は、「map(Map.Entry::getKey) が“キー抽出”そのものを表している」ことです。
filter で対象を絞り、map(getKey) でキーだけに変換する、という流れに慣れておくと、
Map を扱う処理がかなり読みやすくなります。


まとめ:Mapキー抽出ユーティリティで身につけてほしい感覚

Mapキー抽出は、
単に「keySet を呼ぶテクニック」ではなく、
「Map という“辞書”から、必要な見出しだけを安全に・意図通りに取り出す技術」です。

keySet() はビューであり、元の Map と中身を共有することを理解する。
独立したコレクションとして扱いたいときは、new ArrayList<>(map.keySet()) のようにコピーを作る。
MapKeys.toListMapKeys.sorted のようなユーティリティにして、null・空・ソート方針を一箇所に閉じ込める。
「値の条件でキーを抽出する」場合は、entrySet().stream()filtermap(getKey) の流れを基本パターンとして覚える。

あなたのコードのどこかに、
毎回 for (var e : map.entrySet()) { if (...) keys.add(e.getKey()); } と手書きしている箇所があれば、
それを一度「Mapキー抽出ユーティリティ+Stream」に置き換えられないか眺めてみてください。

その小さな整理が、
「Map から“欲しいキーだけ”を迷いなく取り出せるエンジニア」への、
確かな一歩になります。

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