「デフォルトMap取得」は「なかったとき、何を返すか」を先に決めておく技
Map を使っていると、ほぼ必ずこういうコードが出てきます。
Integer count = map.get(key);
if (count == null) {
count = 0;
}
Java「キーがなかったら 0 にしたい」「null を扱いたくない」。
この「なかったとき、どうするか」を毎回 if で書くのはダルいし、バグの元にもなります。
そこで使うのが「デフォルトMap取得」です。
「キーがなかったら、この値(あるいはこのロジック)を使う」と、あらかじめ決めておく考え方です。
基本形1:getOrDefault で「なかったら固定値」を返す
if-null パターンを一行にまとめる
Java 8 以降、Map#getOrDefault が使えます。
import java.util.HashMap;
import java.util.Map;
public class GetOrDefaultBasic {
public static void main(String[] args) {
Map<String, Integer> scores = new HashMap<>();
scores.put("山田", 80);
int yamada = scores.getOrDefault("山田", 0);
int sato = scores.getOrDefault("佐藤", 0);
System.out.println(yamada); // 80
System.out.println(sato); // 0(キーがないのでデフォルト)
}
}
Javaここでの重要ポイントは二つです。
一つ目は、「キーが存在しないときに返す“固定値”を第二引数で指定できる」ことです。getOrDefault("佐藤", 0) は、「佐藤がいなければ 0」と一行で書けています。
二つ目は、「キーが存在しても、その値が null の場合は null が返る」という点です。getOrDefault は“キーがないとき”だけデフォルトを使います。
「値が null のときもデフォルトにしたい」なら、別途工夫が必要です(後述)。
基本形2:computeIfAbsent で「なかったら作って入れて返す」
「デフォルト値を返す」と同時に「Map にも登録したい」場合
集計やカウントでよくあるのが、こういうパターンです。
Integer count = map.get(key);
if (count == null) {
count = 0;
}
map.put(key, count + 1);
Javaこれを computeIfAbsent でスッキリ書けます。
import java.util.HashMap;
import java.util.Map;
public class ComputeIfAbsentBasic {
public static void main(String[] args) {
Map<String, Integer> counts = new HashMap<>();
String key = "apple";
int newCount = counts.computeIfAbsent(key, k -> 0) + 1;
counts.put(key, newCount);
System.out.println(counts.get("apple")); // 1
}
}
Javaもう少し実用的に書くなら、こうです。
int increment(String key, Map<String, Integer> counts) {
int current = counts.getOrDefault(key, 0);
int next = current + 1;
counts.put(key, next);
return next;
}
Javaあるいは、merge を使うとさらに一行で書けますが、ここでは「デフォルト取得」の話に絞ります。
computeIfAbsent の本質は、「キーがなかったら“計算して作った値”を Map に入れて、その値を返す」です。
V value = map.computeIfAbsent(key, k -> createDefaultValue(k));
Javaここでの重要ポイントは三つです。
一つ目は、「デフォルト値を“その場で計算できる”」ことです。k -> new ArrayList<>() のように、キーに応じて新しいオブジェクトを作れます。
二つ目は、「作ったデフォルト値を Map にも登録してくれる」ことです。
次回同じキーで呼んだときは、もう一度作らずに既存の値を返してくれます。
三つ目は、「“なかったら作る”というパターンを一箇所に閉じ込められる」ことです。
これにより、呼び出し側は「とにかくこのキーの値が欲しい。なければ作ってくれればいい」という意図だけを書けます。
よくある実務パターン:Map<K, List<V>> で「なかったら空リストを作る」
グルーピングや集約で頻出する「リストのデフォルト取得」
例えば、「カテゴリ → 商品一覧」のような Map を手で組み立てるとき、
こんなコードを書きがちです。
Map<String, List<Product>> map = new HashMap<>();
for (Product p : products) {
List<Product> list = map.get(p.getCategory());
if (list == null) {
list = new ArrayList<>();
map.put(p.getCategory(), list);
}
list.add(p);
}
Javaこれを computeIfAbsent を使って整理すると、こうなります。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GroupingSample {
public static void main(String[] args) {
Map<String, List<String>> categoryToItems = new HashMap<>();
add(categoryToItems, "果物", "りんご");
add(categoryToItems, "果物", "みかん");
add(categoryToItems, "野菜", "にんじん");
System.out.println(categoryToItems);
// {果物=[りんご, みかん], 野菜=[にんじん]}
}
private static void add(Map<String, List<String>> map,
String category,
String item) {
List<String> list =
map.computeIfAbsent(category, k -> new ArrayList<>());
list.add(item);
}
}
Javaここで深掘りしたい重要ポイントは三つです。
一つ目は、「computeIfAbsent(category, k -> new ArrayList<>()) が、“なかったら空のリストを作って入れる”を一行で表現している」ことです。
二つ目は、「呼び出し側は“とにかくリストを取って add する”だけに集中できる」ことです。add メソッドの中身は、「リストを取得(なければ作る)→ 要素を追加」という自然な流れになっています。
三つ目は、「“デフォルト値の生成ロジック”をラムダとして渡せるので、テストや差し替えがしやすい」ことです。k -> new ArrayList<>() の部分を別メソッドに切り出すこともできます。
「値が null のときもデフォルトにしたい」場合
getOrDefault だけでは足りないケース
getOrDefault は「キーが存在しないとき」にだけデフォルトを返します。
しかし、Map に「キーはあるが値が null」というケースもありえます。
Map<String, Integer> map = new HashMap<>();
map.put("A", null);
int v = map.getOrDefault("A", 0); // v は null(オートアンボクシングで NPE の危険)
Java「キーがあっても値が null ならデフォルトにしたい」なら、ユーティリティを一枚かませると安全です。
public final class DefaultMaps {
private DefaultMaps() {}
public static <K, V> V getOr(
Map<K, V> map,
K key,
V defaultValue
) {
if (map == null) {
return defaultValue;
}
V value = map.get(key);
return value != null ? value : defaultValue;
}
}
Java使い方はこうです。
Integer v = DefaultMaps.getOr(map, "A", 0);
Javaここでの重要ポイントは、
「“キーがない”と“値が null”の両方を、同じようにデフォルト扱いにしている」ことです。
業務的に「null は意味のある値なのか」「単なる未設定なのか」を決めたうえで、
このようなユーティリティを使うかどうか判断するとよいです。
まとめ:デフォルトMap取得ユーティリティで身につけてほしい感覚
デフォルトMap取得は、
単に「便利メソッドを知る」話ではなく、
「“なかったときどうするか”を設計として先に決めて、コードに刻む技術」です。
キーがないときに固定値を返したいなら getOrDefault。
キーがないときに“作って入れて返す”なら computeIfAbsent。Map<K, List<V>> のような構造では、「なかったら空リストを作る」パターンをユーティリティ化する。
「値が null のときもデフォルト扱いにするかどうか」を、プロジェクトとして決めておく。
あなたのコードのどこかに、
毎回 if (map.get(key) == null) { ... } と書いている箇所があれば、
それを一度「デフォルトMap取得ユーティリティ」に置き換えられないか眺めてみてください。
その小さな整理が、
「null と“なさ”を、落ち着いてコントロールできるエンジニア」への、
確かな一歩になります。
