4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Java】HashMapのおさらいと注意点

Last updated at Posted at 2025-03-27

はじめに

こちらは、自分用にまとめたHashMapに関する備忘録です。
個人的なメモとして書いているため、内容に誤りがあるかもしれません。その点を理解した上で参考にしていただけると嬉しいです。
何かあればご意見お寄せいただけると幸いです。

HashMapとは

まず、キーと値の組み合わせで構成されるMapインターフェースがあります。
HashMapはそれを実装した具象クラスで、Java標準のjava.util パッケージに含まれています。
名前の通りハッシュ値を利用することで高速なデータの検索や取得が可能です。
特徴は以下の通り。

  • 重複したキーは持てません
  • 順番は持ちません
  • nullキーは一つまで・null値は複数格納可能です

HashMap利用時の注意点

  • キーのハッシュ値の衝突(重複)が発生すると検索効率が低下します
  • ハッシュ表のサイズはあらかじめ決まっています
    • 頻繁なリサイズはパフォーマンス低下の原因になるため、次々に要素を追加していく処理には注意すべきです

ちょっと便利なメソッド

containsValue

指定した値がHashMapに含まれているかを判定します。

Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
System.out.println(map.containsValue("value1")); // true

computeIfPresent/computeIfAbsent

指定されたキーが存在する/しない場合にのみ処理を行います。

Map<String, Integer> map = new HashMap<>();
map.put("key1", 1);
map.computeIfPresent("key1", (key, value) -> value + 1);
System.out.println(map.get("key1"));  // 出力: 2

putIfAbsent

通常のputはすでに存在するキーに対して値を上書きしますが、putIfAbsentを使うと、キーが存在しない場合のみ値を追加できます。

Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.putIfAbsent("key1", "value2");
System.out.println(map.get("key1")); // value1(上書きされない)

値の計算が発生&戻り値を使用したい場合はcomputeIfAbsentが適していますが、こちらの方が読みやすいと思っています。

merge

キーが存在しない場合は第2引数をセットし、存在する場合は指定のルールで値を更新します。putputIfAbsentの複合のようなものですね。以前の値をベースに更新していく処理にいいと思います。
if文で条件分岐が必要ないのが助かります。

Map<String, Integer> map = new HashMap<>();
map.put("key1", 1);

map.merge("key1", 5, (v1, v2) -> v1 + v2);
System.out.println(map); // {key1=6}

getOrDefault

キーが存在しない場合にデフォルト値を返します。いちいちcontainsKeyに聞きに行かなくて済みます。

Map<String, String> map = new HashMap<>();
System.out.println(map.getOrDefault("key1", "default")); // default

おまけ サイズの宣言

例えばループで大量のデータを追加する場合、リサイズが何度も発生してしまいます。

Map<Integer, String> map = new HashMap<>();
for (int i = 0; i < 100000; i++) {
    map.put(i, "value" + i); // 途中で何度もリサイズが発生
}

new HashMap<>(100000)のように予めサイズを宣言すれば不要なリサイズを防げます。

ハマりがちな落とし穴

forEachはHashMapがnullのときにこける

Optionalを使うことで回避できます。

Optional.ofNullable(map).ifPresent(m -> m.forEach((key, value) ->
    System.out.println(key + ": " + value)
));

equalsの挙動

これはHashMapに限らずですが、equals比較が思うように動作しないことがあるため注意が必要です。Arrayを使用するときが代表例です。

Map<String, String[]> map1 = new HashMap<>();
map1.put("key1", new String[]{"value1", "value2"});

Map<String, String[]> map2 = new HashMap<>();
map2.put("key1", new String[]{"value1", "value2"});

System.out.println(map1.equals(map2)); // false

この例では、配列の参照が異なるためequals比較が失敗します。Arrays.equalsを使用するか、ListやSetなどほかのクラスを使用しましょう。

System.out.println(Arrays.equals(map1.get("key1"), map2.get("key1"))); // true

補足

HashMap以外にどのようなMapがあるの?というとjava.utilにはLinkedHashMap, TreeMapなどがあります。

  • LinkedHashMap:キーを挿入もしくはアクセス順で保存してくれます
  • TreeMap:キーを(指定の規則にしたがって)自動的にソートしてくれます

自分は使用したことがありませんが、順序が重要な場合は別クラスの使用を検討したほうが良さそうです。

最後に

HashMapは自分にとって直感的に使用でき便利ですが、改めて仕様を確認したことにより、リサイズのコストなど注意すべき点もあることに気付きました。
普段何気なく使っているクラスでも、きちんと理解して、効率的に活用できるようになりたいですね~

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?