🗺️

Java 8以降でMapに追加されたメソッドたち

に公開

この記事は何?

Javaでマップはちょいちょい使いますね。この記事では、Java 8以降で導入された比較的新しいメソッドを中心に紹介します。

環境

JDK 21

超基本的な使い方

put()で値の追加、get()で値の取得ができますね。

// マップを生成
Map<String, Integer> fruitsMap = new HashMap<>();
// マップにフルーツの名前と数量を追加
fruitsMap.put("apple", 10);
fruitsMap.put("banana", 20);
fruitsMap.put("orange", 30);
// リンゴの数量を取得
int appleCount = fruitsMap.get("apple");
System.out.println("リンゴの数量: " + appleCount);
実行結果
リンゴの数量: 10

マップの生成は Map.of() で簡潔に書くこともできます。ただし、これはイミュータブル(不変)なマップを生成するので、後から値を追加することはできません(put()するとjava.lang.UnsupportedOperationExceptionがスローされます)。

// マップを生成
Map<String, Integer> fruitsMap = Map.of(
    "apple", 10,
    "banana", 20,
    "orange", 30
);
// リンゴの数量を取得
int appleCount = fruitsMap.get("apple");
System.out.println("リンゴの数量: " + appleCount);

他にもこんなメソッドがあります。

  • containsKey() : キーが存在すれば true を返す
  • containsValue() : 値が存在すれば true を返す
  • size() : マップのサイズを取得
  • isEmpty() : マップが空であれば true を返す
  • remove() : キーと値のペアを削除

マップの内容を全て表示する

マップの内容を全て表示するには、forEach()メソッドを使います。

ただし、マップは基本的に順序を保証しないため、マップ生成時に指定した順に表示されるとは限りません。

// マップを生成
Map<String, Integer> fruitsMap = Map.of(
    "apple", 10,
    "banana", 20,
    "orange", 30
);
// マップの内容を表示
fruitsMap.forEach((k, v) -> System.out.println("フルーツ: " + k + ", 数量: " + v));
実行結果
フルーツ: banana, 数量: 20
フルーツ: orange, 数量: 30
フルーツ: apple, 数量: 10

マップをリストに変換する

マップの内容をリストに変換するには、entrySet()メソッドを使ってエントリーセットを取得し、それをストリームに変換してリストに収集します。

エントリーは、マップのキーと値のペアを保持するものです。エントリーセットは、マップの全ペアのエントリーを保持します。

今回は、フルーツの名前と数量を持つ FruitStock クラスを定義し、それを使ってマップの内容をリストに変換します。

// フルーツの名前と数量を持つレコード
record FruitStock(String name, int quantity) {
}
// マップを生成
Map<String, Integer> fruitsMap = Map.of(
    // マップにフルーツの名前と数量を追加
    "apple", 10,
    "banana", 20,
    "orange", 30
);
// マップをリストに変換
List<FruitStock> stockList = fruitsMap.entrySet()
    .stream()
    .map(entry -> new FruitStock(entry.getKey(), entry.getValue()))
    .toList();
System.out.println("フルーツの在庫リスト:" + stockList);
実行結果
フルーツの在庫リスト:[FruitStock[name=apple, quantity=10], FruitStock[name=orange, quantity=30], FruitStock[name=banana, quantity=20]]

キーが存在しない場合にデフォルト値を返す

get()の引数に指定したキーがマップに存在しない場合、nullが返されます。getOrDefault()メソッドを使うと、キーが存在しない場合にデフォルト値を返すことができます。

// マップを生成
Map<String, Integer> fruitsMap = Map.of(
    // マップにフルーツの名前と数量を追加
    "apple", 10,
    "banana", 20,
    "orange", 30
);
// キーが存在するので値10を返す
int appleCount = fruitsMap.getOrDefault("apple", 0);
System.out.println("リンゴの数量: " + appleCount);
// キーが存在しないのでデフォルト値0を返す
int melonCount = fruitsMap.getOrDefault("melon", 0);
System.out.println("メロンの数量: " + melonCount);
実行結果
リンゴの数量: 10
メロンの数量: 0

キーが存在しない場合のみ値を追加する

putIfAbsent()メソッドを使うと、キーが存在しない場合にのみ値を追加することができます。キーが既に存在する場合は、何もしません。

put()メソッドは、キーが既に存在する場合は値を上書きします。

// マップを生成(マップを変更可能にしたいので、Map.of()は使わない)
Map<String, Integer> fruitsMap = new HashMap<>();
// マップにフルーツの名前と数量を追加
fruitsMap.put("apple", 10);
fruitsMap.put("banana", 20);
fruitsMap.put("orange", 30);
// grapeは存在しないのでマップに追加される
fruitsMap.putIfAbsent("grape", 40);
// appleは存在するのでマップに追加されない(数量は10のまま)
fruitsMap.putIfAbsent("apple", 50);
// マップの内容を表示
fruitsMap.forEach((k, v) -> System.out.println("フルーツ: " + k + ", 数量: " + v));
実行結果
フルーツ: banana, 数量: 20
フルーツ: orange, 数量: 30
フルーツ: apple, 数量: 10
フルーツ: grape, 数量: 40

マップに値を追加しつつ取得する

追加した値をすぐに取得したい場合は、compute()メソッドを使います。第1引数にはキーを指定します。第2引数には、キーと現在の値を引数に取り、新しい値を返すラムダ式を指定します。

// マップを生成(マップを変更可能にしたいので、Map.of()は使わない)
Map<String, Integer> fruitsMap = new HashMap<>();
// マップにフルーツの名前と数量を追加
fruitsMap.put("apple", 10);
fruitsMap.put("banana", 20);
fruitsMap.put("orange", 30);
// grapeは存在しないのでマップに追加される
// k = "grape", v = null
int grapeCount = fruitsMap.compute("grape", (k, v) -> 40);
System.out.println("ブドウの数量: " + grapeCount);
// appleは存在するので値が50に更新される
// k = "apple", v = 10
int appleCount = fruitsMap.compute("apple", (k, v) -> 50);
System.out.println("リンゴの数量: " + appleCount);
実行結果
ブドウの数量: 40
リンゴの数量: 50

キーが存在しない場合のみ、マップに値を追加しつつ取得する

computeIfAbsent()メソッドを使います。第1引数にはキーを指定します。第2引数には、キーを引数に取り、新しい値を返すラムダ式を指定します。

// マップを生成(マップを変更可能にしたいので、Map.of()は使わない)
Map<String, Integer> fruitsMap = new HashMap<>();
// マップにフルーツの名前と数量を追加
fruitsMap.put("apple", 10);
fruitsMap.put("banana", 20);
fruitsMap.put("orange", 30);
// grapeは存在しないのでマップに追加される
int grapeCount = fruitsMap.computeIfAbsent("grape", k -> 40);
System.out.println("ブドウの数量: " + grapeCount);
// appleは存在するのでマップは更新されず、現在の値10が返る
int appleCount = fruitsMap.computeIfAbsent("apple", k -> 50);
System.out.println("リンゴの数量: " + appleCount);
実行結果
ブドウの数量: 40
リンゴの数量: 10

キーが存在する場合のみ、マップに値を追加しつつ取得するcomputeIfPresent()メソッドもあります。

合わせて読みたい

https://zenn.dev/masatoshi_tada/articles/0ea1d358b0e82f#hashcode()メソッドとは?

Discussion