導入
Javaのコレクション操作において、Mapに値を格納する際「キーが存在しなければ追加、存在すれば既存値と統合(加算や更新など)」という処理は非常に頻出します。従来のif-else文やcontainsKeyを用いた実装では、コードが冗長になりがちでした。Java 8で導入されたMap.mergeメソッドを活用することで、これらの処理を1行で記述でき、可読性と保守性を大幅に向上させることが可能です。
基礎知識
Map.merge(K key, V value, BiFunction remappingFunction)は、指定されたキーがマップ内に存在しない場合、そのキーと値を関連付けます。もしキーが既に存在する場合、第3引数のBiFunction(再マッピング関数)が実行され、「既存の値」と「新しい値」を組み合わせて新しい値を生成し、それをマップに上書きします。これにより、条件分岐を意識せずにスマートに状態を更新できます。
実装/解決策
実務では、単なる値の更新だけでなく、リストへの要素追加や数値の加算、あるいはオブジェクトのプロパティ更新などに利用します。特に、ストリーム処理で集計を行う際、Collectors.toMapの第3引数としてmergeのロジックを組み込むケースが非常に多いため、この考え方をマスターしておくと集計処理の幅が広がります。
サンプルプログラム
以下のコードは、商品リストからカテゴリごとの在庫数を集計する例です。Map.mergeを使用することで、非常に簡潔に記述しています。
import java.util.HashMap;
import java.util.Map;
public class MergeExample {
public static void main(String[] args) {
Map inventory = new HashMap<>();
// サンプルデータ:商品名と入荷数
Map arrivals = Map.of(
"Apple", 10,
"Banana", 20,
"Apple", 5
);
// Map.mergeを用いた在庫集計
arrivals.forEach((item, count) -> {
// キーが存在しない場合はcountをそのまま格納
// 存在する場合は、既存のcurrentValueと新しいcountを加算して更新
inventory.merge(item, count, (currentValue, newValue) -> currentValue + newValue);
});
// 結果の出力
inventory.forEach((k, v) -> System.out.println(k + ": " + v));
// 出力結果: Apple: 15, Banana: 20
}
}
応用・注意点
現場で活用する際の重要な注意点が2つあります。
1つ目は、nullの扱いです。remappingFunctionがnullを返した場合、そのキーはマップから削除されます。意図しない削除を防ぐため、関数内でnullチェックを行うか、そもそもnullを許容しないMap実装(ConcurrentHashMapなど)を検討してください。
2つ目は、スレッドセーフ性です。HashMapはスレッドセーフではありません。マルチスレッド環境下でmergeを使用する場合は、ConcurrentHashMapを選択してください。ConcurrentHashMapのmergeはアトミック(不可分)な操作として保証されているため、ロックを意識することなく安全にカウントアップなどの処理が行えます。
これらのポイントを抑えることで、より堅牢でモダンなJavaアプリケーションの構築が可能になります。ぜひ日々の実装で活用してください。

コメント