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

Java Java
スポンサーリンク

「Map値抽出」は“辞書の中身だけを取り出す”イメージ

Map<K, V> は「キー → 値」の対応表です。
業務では「ユーザーID → ユーザー情報」「商品コード → 商品情報」など、ほぼ必ず出てきます。

その中で「値だけ欲しい」「キーはいらない」という場面がかなり多いです。
たとえば「ユーザー情報一覧が欲しい」「商品情報だけを別処理に渡したい」など。

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


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

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

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

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

public class ValuesBasic {

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

        Collection<String> values = userMap.values();

        System.out.println(values); // [山田, 佐藤, 鈴木] のようなイメージ
    }
}
Java

ここで重要なのは、「values() も“ビュー”である」という点です。
ビューというのは、「元の Map と中身を共有している“見え方だけのコレクション”」という意味です。

つまり、userMap に要素を追加・削除すると、values にも反映されます。
逆に、values.remove("山田") のように値コレクションから削除すると、元の userMap からもそのエントリが消えます。

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


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

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

values() の戻り値は Collection<V> です。
しかし業務では、「List として扱いたい」ことが多いです。
たとえば「ソートしたい」「先頭 N 件だけ取りたい」「Stream に流したい」など。

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

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

public final class MapValues {

    private MapValues() {}

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

使い方はこうです。

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

List<String> userNames = MapValues.toList(userMap);

System.out.println(userNames); // [山田, 佐藤, 鈴木] など
Java

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

一つ目は、「new ArrayList<>(map.values()) で“独立した 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 MapValues {

    private MapValues() {}

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

使い方はこうです。

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

List<Integer> sortedScores = MapValues.sorted(scores);
// [70, 80, 90]
Java

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

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

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

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

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

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

「値だけ欲しい」といっても、
「全部」ではなく「特定のキーに合致する値だけ欲しい」こともあります。

たとえば「ID が特定のパターンのものだけの値一覧」などです。

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

public final class MapValues {

    private MapValues() {}

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

使い方の例です。

Map<String, Integer> scores = Map.of(
        "A001", 80,
        "B002", 90,
        "A003", 70
);

// キーが "A" で始まるもののスコアだけ
List<Integer> aScores =
        MapValues.valuesWhereKey(scores, k -> k.startsWith("A"));

System.out.println(aScores); // 例: [80, 70]
Java

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

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

二つ目は、「Predicate<? super K> keyCondition が“どんなキーのときに値を採用するか”を表している」ことです。
k -> k.startsWith("A") のように、業務ルールをラムダで渡せます。

三つ目は、「値抽出とフィルタをユーティリティに閉じ込めることで、呼び出し側のコードが“何をしたいか”だけに集中できる」ことです。


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

「Map → values / entrySet → 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<Integer> allScores =
        scores.values().stream()
              .collect(Collectors.toList());

// キー条件で絞った値だけ
List<Integer> yamadaScoreOnly =
        scores.entrySet().stream()
              .filter(e -> e.getKey().equals("山田"))
              .map(Map.Entry::getValue)
              .collect(Collectors.toList());
Java

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

一つ目は、「全部の値が欲しいだけなら values().stream() で十分」ということです。

二つ目は、「キーや値の条件で絞りたいときは entrySet().stream()filtermap(getValue) が基本形」ということです。
このパターンに慣れると、Map を使った処理がかなり読みやすくなります。


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

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

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

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

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

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