JavaのMapをマスターしよう!基本概念から実装まで初心者向けに解説

この記事のポイント

JavaのMapは、データを効率的に管理するために頻繁に使われるデータ構造です。

この記事を読むと、次のようなことがわかります。

  • Mapの基本的な仕組み
  • HashMap、TreeMap、LinkedHashMapの違いと、それぞれの使いどころ
  • put()、get()、remove()などの基本操作
  • さまざまなループ処理の方法
  • putIfAbsent()やmerge()などの応用的なメソッドの実践例

Mapを使いこなせるようになると、データの検索や管理が効率的にできるようになります。

キーと値のペアという考え方は、辞書やデータベースなど、身近なところでも使われている重要な概念です。

この記事で基本から応用まで段階的に学ぶことで、実際の開発で使える実践的なスキルを身に付けることができるでしょう。

目次

Mapの基本概念と特徴

Mapは、Javaでデータを管理する際に非常によく使われるデータ構造の一つです。

「キーと値のペア」という形でデータを保存することで、辞書のように「言葉を調べたら意味が出てくる」ような使い方ができます。

ここでは、Mapとは何か、他のコレクション(ListやSet)との違い、そして代表的な実装クラスについて学んでいきましょう。

【関連】
Javaをもっと詳しく学ぶならpaizaラーニング

Mapの概要と役割

Mapは、一意な(重複しない)キーに対して値を関連付けて保存する仕組みです。

例えば、動物の名前(キー)と年齢(値)を管理したい場合を考えてみましょう。「ライオン」というキーに対して「5歳」という値を結びつけて保存できます。

この仕組みの便利なところは、大量のデータの中から、キーを使って目的の値を瞬時に取り出せることです。

Mapの注意点として、次の2つの重要なルールがあります。

  • キーは重複できない(同じキーは1つだけ)
  • 値は重複してもいい(複数のキーが同じ値を持てる)

実際のコード例を見てみましょう。

import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Integer> animalAges = new HashMap<>(); animalAges.put("ライオン", 5); animalAges.put("ゾウ", 10); System.out.println(animalAges); } }

出力結果

{ライオン=5, ゾウ=10}

この例では、Map<String, Integer>という形で「キーはString型(文字列)、値はInteger型(整数)」と宣言しています。

その後、put()メソッドで、キーと値のペアを追加しています。

List・Setとの違い

JavaにはMap以外にも、データをまとめて扱うコレクションがあります。コレクションとは、複数の要素(データ)をひとまとまりにして管理するためのオブジェクトのことです。

データを扱うための「入れ物」だと考えるとわかりやすいと思います。

それぞれの違いを下記に整理しましたので、ここで理解しておきましょう。

それぞれの特徴

  • List:順番を保持し、重複を許可する。インデックス(番号)でデータにアクセスする
  • Set:順番は保持せず、重複を許可しない。値そのものでデータを管理する
  • Map:キーと値のペアで管理。キーは重複不可、値は重複できる

アクセス方法の違い

  • List:list.get(0) のようにインデックスでアクセス
  • Set:特定の値が含まれるかを確認する用途が多い

Map:map.get("ライオン") のようにキーでアクセス

import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; public class Main { public static void main(String[] args) { List<String> animalList = Arrays.asList("ライオン", "ゾウ"); Set<String> animalSet = new HashSet<>(animalList); Map<String, String> animalTypes = new HashMap<>(); animalTypes.put("ライオン", "肉食動物"); animalTypes.put("ゾウ", "草食動物"); System.out.println("List: " + animalList); System.out.println("Set: " + animalSet); System.out.println("Map: " + animalTypes); } }

出力結果

List: [ライオン, ゾウ]
Set: [ライオン, ゾウ]
Map: {ライオン=肉食動物, ゾウ=草食動物}

Listは順番通りに表示され、Setも要素を保持し、Mapはキーと値のペアで表示されています。

それぞれの用途に応じて使い分けることが大切です。

Mapの主な実装クラス

Javaには、Mapを実装した3つの主要なクラスがあります。

それぞれの特徴を確認しておきましょう。

HashMap(最も一般的)
最も高速で、通常はこれを使います。ただし、要素の順序は保証されません。データを追加した順番と、取り出す順番が異なる場合があります。

TreeMap(自動ソート)
キーを自動的にソート(並び替え)して保存します。文字列なら辞書順(あいうえお順、ABC順)、数値なら小さい順に自動的に並びます。

LinkedHashMap(順序を保持)
要素を追加した順番を記憶します。データを入れた順番通りに取り出したい場合に使います。

import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; public class Main { public static void main(String[] args) { Map<String, String> hashMap = new HashMap<>(); Map<String, String> treeMap = new TreeMap<>(); Map<String, String> linkedMap = new LinkedHashMap<>(); hashMap.put("ゾウ", "大型"); hashMap.put("ライオン", "中型"); treeMap.put("ゾウ", "大型"); treeMap.put("ライオン", "中型"); linkedMap.put("ゾウ", "大型"); linkedMap.put("ライオン", "中型"); System.out.println("HashMap: " + hashMap + " // 順序不定"); System.out.println("TreeMap: " + treeMap + " // キー順ソート"); System.out.println("LinkedHashMap: " + linkedMap + " // 挿入順"); } }

出力結果

HashMap: {ライオン=中型, ゾウ=大型}  // 順序不定
TreeMap: {ゾウ=大型, ライオン=中型}  // キー順ソート
LinkedHashMap: {ゾウ=大型, ライオン=中型}  // 挿入順

同じデータを入れても、実装クラスによって出力される順序が異なります。

用途に応じて適切なクラスを選ぶことが重要です。

Mapの基本操作

Mapを使いこなすには、基本的な操作方法をしっかり理解する必要があります。

ここでは、要素の追加・取得・削除といった基本操作から、キーや値が存在するかを確認する方法まで、実際のコード例とともにくわしく解説していきます。

このような操作は、Mapを使う上で最も頻繁に使用するので、ここでマスターしてしまいましょう。

要素の追加と上書き(put)

put()メソッドを使って、Mapにキーと値のペアを追加します。
その際、新規追加か変更なのかで挙動が少し異なります。

  • 新しいキーの場合:そのまま追加され、nullが返される
  • 既存のキーの場合:値が上書きされ、古い値が返される

この戻り値を利用することで、「新規追加なのか、更新なのか」を判断できます。

実際のコード例を見てみましょう。

import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Integer> animalWeights = new HashMap<>(); Integer oldValue = animalWeights.put("ライオン", 200); System.out.println("以前の値: " + oldValue); Integer previousValue = animalWeights.put("ライオン", 220); System.out.println("更新前の値: " + previousValue); System.out.println("現在のMap: " + animalWeights); } }

出力結果

以前の値: null
更新前の値: 200
現在のMap: {ライオン=220}

最初のput()では新規追加なのでnullが返されます。

2回目のput()では、既に「ライオン」というキーが存在するため、古い値(200)が返され、新しい値(220)で上書きされます。

要素の取得(get)

get()メソッドを使って、キーに対応する値を取り出します。

もし、存在しないキーを指定するとnullが返されます。
ただし、nullが返された場合、「キーが存在しない」のか「値としてnullが保存されている」のかを区別できません
くわしくは後述しますが、containsKey()メソッドと組み合わせて使うことで、キーの存在を確認できます。

ちなみに、Java8以降ではgetOrDefault()メソッドを使うと、キーが存在しない場合にデフォルト値を指定できます。

実際のコード例を見てみましょう。

import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, String> animalSounds = new HashMap<>(); animalSounds.put("ライオン", "ガオー"); animalSounds.put("ゾウ", null); String lionSound = animalSounds.get("ライオン"); String elephantSound = animalSounds.get("ゾウ"); String dogSound = animalSounds.getOrDefault("イヌ", "ワンワン"); System.out.println("ライオンの鳴き声: " + lionSound); System.out.println("ゾウの鳴き声: " + elephantSound); System.out.println("イヌの鳴き声: " + dogSound); } }

出力結果

ライオンの鳴き声: ガオー
ゾウの鳴き声: null
イヌの鳴き声: ワンワン

「ライオン」は値が存在するので「ガオー」が取得できます。「ゾウ」は値としてnullが保存されているのでnullが返されます。

「イヌ」はキー自体が存在しないので、getOrDefault()で指定したデフォルト値「ワンワン」が返されます。

要素の削除(remove)

remove()メソッドを使って、Mapから要素を削除することができます。キーのみを指定する方法と、キーと値の両方を指定する方法があります。

  • キーのみ指定:remove(キー) → 該当するキーの要素を削除する
  • キーと値を両方指定:remove(キー, 値) → 組み合わせが完全一致する場合のみ削除する

キーのみの場合は削除された値が返され、キーと値を両方指定した場合は削除の成否(true/false)が返されます。

実際のコード例を見てみましょう。

import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, String> animalHabitats = new HashMap<>(); animalHabitats.put("ライオン", "サバンナ"); animalHabitats.put("ペンギン", "南極"); String removed = animalHabitats.remove("ライオン"); boolean removed2 = animalHabitats.remove("ペンギン", "北極"); System.out.println("削除された値: " + removed); System.out.println("条件削除の結果: " + removed2); System.out.println("残りのMap: " + animalHabitats); } }

出力結果

削除された値: サバンナ
条件削除の結果: false
残りのMap: {ペンギン=南極}

「ライオン」はキーのみを指定して削除したので成功し、削除された値「サバンナ」が返されます。

「ペンギン」は値として「北極」を指定しましたが、実際の値は「南極」なので一致せず、削除されないためfalseが返されるという結果になります。

キーや値の確認(containsKey・containsValue)

キーや値が存在するかを確認するには、専用のメソッドを使います。

  • containsKey(キー):指定したキーが存在するか確認する
  • containsValue(値):指定した値が存在するか確認する

 containsKey()はハッシュという仕組みを使うため非常に高速ですが、containsValue()はすべての値を順番に確認するため時間がかかります。

実際のコード例を見てみましょう。

import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Integer> animalSpeeds = new HashMap<>(); animalSpeeds.put("チーター", 100); animalSpeeds.put("ウサギ", 50); boolean hasCheetah = animalSpeeds.containsKey("チーター"); boolean hasSpeed100 = animalSpeeds.containsValue(100); boolean hasLion = animalSpeeds.containsKey("ライオン"); System.out.println("チーターがいるか: " + hasCheetah); System.out.println("時速100kmの動物がいるか: " + hasSpeed100); System.out.println("ライオンがいるか: " + hasLion); } }

出力結果

チーターがいるか: true
時速100kmの動物がいるか: true
ライオンがいるか: false

「チーター」というキーは存在するのでtrue、値として100は存在するのでtrue、「ライオン」というキーは存在しないのでfalseが返されます。

ちなみに、これらのメソッドは条件分岐で「存在する場合のみ処理を実行する」といった場面でよく使われます。

Mapの繰り返し処理

Mapに保存されたデータをすべて処理したいという場面には、よく遭遇します。

例えば、「すべての動物の名前と年齢を表示したい」といったケースです。

Mapの繰り返し処理には、キーだけを取り出す方法、値だけを取り出す方法、キーと値の両方を取り出す方法など、いくつかのパターンがあります。

ここでは、それぞれの方法を具体的なコード例とともに解説します。

keySet・entrySet・valuesの基本

Mapから要素を取り出すやり方として、主に次の3つの方法があります。

  • keySet():キーだけを取り出す
  • values():値だけを取り出す
  • entrySet():キーと値のペアを取り出す

どれを使うべきかは、次のようなケースに応じて判断してください。

  • キーだけが必要な場合 → keySet()
  • 値だけが必要な場合 → values()
  • キーと値の両方が必要な場合 → entrySet()

実際のコード例を見てみましょう。

import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; public class Main { public static void main(String[] args) { Map<String, Integer> animalLegs = new HashMap<>(); animalLegs.put("イヌ", 4); animalLegs.put("ニワトリ", 2); Set<String> keys = animalLegs.keySet(); Collection<Integer> values = animalLegs.values(); Set<Map.Entry<String, Integer>> entries = animalLegs.entrySet(); System.out.println("キー: " + keys); System.out.println("値: " + values); System.out.println("エントリー: " + entries); } }

出力結果(例)

キー: [ニワトリ, イヌ]
値: [2, 4]
エントリー: [ニワトリ=2, イヌ=4]

keySet()を使うと動物の名前だけ、values()を使うと足の本数だけ、entrySet()を使うと両方がセットで取得できているのが確認できますね。

拡張for文によるMapのループ処理

拡張for文(for-each文)を使うと、Mapの要素を簡潔に繰り返し処理できます。

entrySet()と組み合わせることで、キーと値の両方に効率的にアクセスできます。この方法は記述が簡単で読みやすいため、基本的なループ処理に最適です。

実際のコード例を見てみましょう。

import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, String> animalColors = new HashMap<>(); animalColors.put("キリン", "黄色"); animalColors.put("ゾウ", "灰色"); for (Map.Entry<String, String> entry : animalColors.entrySet()) { System.out.println(entry.getKey() + "は" + entry.getValue()); } } }

出力結果

キリンは黄色
ゾウは灰色

entry.getKey()でキー(動物の名前)を取得し、entry.getValue()で値(色)を取得しています。この書き方は、すべての要素を順番に処理したい場合に非常に便利です。

ちなみに、拡張for文の基本的な書き方は

for (型 変数名 : コレクション)

という形です。
この構文を使うことで、コレクション内の要素を順番に取り出して処理できます。

Iteratorを使ったループ処理

Iterator(反復子)を使うと、ループ中に安全に要素を削除できます。

拡張for文でループ中に要素を削除しようとすると、エラーが発生する可能性があります。しかし、Iteratorのremove()メソッドを使えば、安全に削除できます。

import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Integer> animalAges = new HashMap<>(); animalAges.put("ライオン", 15); animalAges.put("ゾウ", 5); Iterator<Map.Entry<String, Integer>> iterator = animalAges.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Integer> entry = iterator.next(); if (entry.getValue() > 10) { iterator.remove(); } } System.out.println("削除後のMap: " + animalAges); } }

出力結果

削除後のMap: {ゾウ=5}

このプログラムでは、年齢が10歳より大きい動物を削除しています。「ライオン」は15歳なので削除され、「ゾウ」は5歳なので残るというわけです。

条件に基づいて要素を削除したい場合は、この方法を使いましょう。

Stream APIによるモダンなMap操作

Java8以降では、Stream APIを使ってより洗練された方法でMapを操作できます。

Stream APIを使うと、filter()(フィルタリング)、map()(変換)、forEach()(繰り返し処理)などのメソッドをつなげて、複雑な処理も簡潔に記述できます。ラムダ式と組み合わせることで、読みやすく保守性の高いコードが書けます。

実際のコード例を見てみましょう。

import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Integer> animalSizes = new HashMap<>(); animalSizes.put("アリ", 1); animalSizes.put("ライオン", 100); animalSizes.entrySet().stream() .filter(entry -> entry.getValue() > 50) .forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue())); } }

出力結果

ライオン: 100

この例では、filter()を使ってサイズが50より大きい動物だけを抽出し、forEach()でその該当する動物を表示しています。

「アリ」はサイズが1なので表示されず、「ライオン」だけが表示されます。Stream APIは、条件に合う要素だけを処理したい場合に非常に便利です。

Mapのソートと順番の制御

Mapを使っていると、要素の順番が気になることがあるかもしれません。Mapの実装クラスによって、要素の順序の扱い方が異なるためです。

  • 順序をまったく保証しない
  • 追加した順番を保持する
  • 自動的にソートする

などといった特徴の違いがあります。

ここでは、各実装クラスの順序に関する特性と、Stream APIを使った柔軟なソート方法について解説します。

Mapの順序と格納順の違い

HashMapはパフォーマンスを優先するため、要素の格納順序を保持しません。

内部では「ハッシュ値」という仕組みを使ってデータを効率的に管理しています。そのため、要素を追加した順番と、取り出される順番が異なります。

「順序は気にしないが、とにかく速く動作してほしい」という場合に最適です。

実際のコード例を見てみましょう。

import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, String> hashMap = new HashMap<>(); hashMap.put("ライオン", "肉食"); hashMap.put("ウサギ", "草食"); hashMap.put("クマ", "雑食"); System.out.println("HashMapの順序:"); hashMap.forEach((k, v) -> System.out.println(k + ": " + v)); } }

出力結果(例)

HashMapの順序:
ウサギ: 草食
クマ: 雑食
ライオン: 肉食

このように、HashMapでは追加した順番は保持されません。

JVMのバージョンや実行環境によって、上記の出力結果と変わる可能性もあります。

LinkedHashMapによる順序保持

LinkedHashMapは、要素を追加した順番を記憶しておくMapです。

HashMapの高速性を維持しながら、内部的に追加順序を記録します。そのため、要素を取り出す際は、追加した順番通りに取得できます。

「処理速度も重要だが、順序も保ちたい」という場合に使います。

実際のコード例を見てみましょう。

import java.util.LinkedHashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Integer> linkedMap = new LinkedHashMap<>(); linkedMap.put("ゾウ", 3000); linkedMap.put("ライオン", 200); linkedMap.put("ウサギ", 2); System.out.println("LinkedHashMapの順序:"); linkedMap.forEach((k, v) -> System.out.println(k + ": " + v + "kg")); } }

出力結果

LinkedHashMapの順序:
ゾウ: 3000kg
ライオン: 200kg
ウサギ: 2kg

このように、取り出すときも追加したときと同じ順序で取得できます。処理結果を元の順序で表示したい場合に便利です。

TreeMapによるキー順ソート

TreeMapは、キーを自動的にソート(並び替え)して保存するMapです。

文字列なら辞書順(あいうえお順、ABC順)、数値なら昇順(小さい順)で自動的に並びます。要素を追加するたびに内部的にソートが維持されるため、常に順序が保たれた状態でデータを取得できます。

実際のコード例を見てみましょう。

import java.util.Map; import java.util.TreeMap; public class Main { public static void main(String[] args) { Map<String, String> treeMap = new TreeMap<>(); treeMap.put("ライオン", "アフリカ"); treeMap.put("カンガルー", "オーストラリア"); treeMap.put("パンダ", "中国"); System.out.println("TreeMapの順序(キー順):"); treeMap.forEach((k, v) -> System.out.println(k + ": " + v)); } }

出力結果

TreeMapの順序(キー順):
カンガルー: オーストラリア
パンダ: 中国
ライオン: アフリカ

自動的にソートされていることが確認できますね。

streamでのソート操作

Stream APIのsorted()メソッドを使うと、既存のMapをさまざまな条件でソートできます。

キーでソート、値でソート、逆順ソートなど、柔軟な並び替えが可能です。重要なのは、元のMapは変更されず、ソート結果を新しいMapとして生成できる点です。

これにより、元のデータを安全に保ったまま、さまざまな順序で表示できます。

実際のコード例を見てみましょう。

import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Integer> animalHeights = new HashMap<>(); animalHeights.put("キリン", 500); animalHeights.put("ライオン", 120); animalHeights.put("ウサギ", 30); Map<String, Integer> sortedByValue = animalHeights.entrySet().stream() .sorted(Map.Entry.comparingByValue()) .collect(LinkedHashMap::new, (m, e) -> m.put(e.getKey(), e.getValue()), Map::putAll); System.out.println("値でソートした結果:"); System.out.println(sortedByValue); } }

出力結果

値でソートした結果:
{ウサギ=30, ライオン=120, キリン=500}

この例では、値(身長)の小さい順にソートしています。

comparingByValue()で値を基準にソートし、結果をLinkedHashMapに格納することで順序を保持しています。
元のanimalHeightsは変更されず、ソートされた新しいMapが作成されます。

Mapの応用テクニック

基本操作に慣れてきたら、より便利な応用メソッドも使ってみましょう。

Java8以降では、条件付きでの要素追加や、既存値の更新・結合など、便利なメソッドが追加されています。こういったメソッドを使いこなすことで、複雑な処理も簡潔に書けるようになります。実際の開発でよく使われる応用テクニックを紹介します。

値が存在しない場合のみ追加

putIfAbsent()メソッドは、キーが存在しない場合にのみ値を追加します。

既にキーが存在する場合は何もせず、既存の値をそのまま保持します。これにより、「初期値を設定したいが、既に値がある場合は上書きしたくない」という処理を、条件分岐なしで実現できます。

import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, String> animalTypes = new HashMap<>(); animalTypes.put("ライオン", "肉食動物"); String result1 = animalTypes.putIfAbsent("ライオン", "大型動物"); String result2 = animalTypes.putIfAbsent("ゾウ", "草食動物"); System.out.println("既存値: " + result1); System.out.println("新規値: " + result2); System.out.println("最終的なMap: " + animalTypes); } }

出力結果

既存値: 肉食動物
新規値: null
最終的なMap: {ライオン=肉食動物, ゾウ=草食動物}

put()メソッドの挙動を覚えているでしょうか。
新規追加か変更かで、次のように挙動が違いました。

  • 新しいキーの場合:そのまま追加され、nullが返される
  • 既存のキーの場合:値が上書きされ、古い値が返される

「ライオン」はすでに「肉食動物」という値が存在するため、「大型動物」で上書きされず、既存の値「肉食動物」が返されます。
「ゾウ」は新規追加なのでnullが返され、「草食動物」が追加されるという流れです。

既存値の更新や結合

merge()メソッドは、既存の値と新しい値を組み合わせて更新します。

キーが存在しない場合は新しい値を追加し、存在する場合は指定した関数で値を結合します。カウンターの実装や文字列の連結など、値を累積的に処理したい場合に便利です。

実際のコード例を見てみましょう。

import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Integer> animalCount = new HashMap<>(); animalCount.put("ライオン", 3); animalCount.merge("ライオン", 2, Integer::sum); animalCount.merge("ゾウ", 5, Integer::sum); System.out.println("ライオン: " + animalCount.get("ライオン")); System.out.println("ゾウ: " + animalCount.get("ゾウ")); System.out.println("最終的なMap: " + animalCount); } }

出力結果

ライオン: 5
ゾウ: 5
最終的なMap: {ライオン=5, ゾウ=5}

Integer::sumは「足し算をする」という指示です。

「ゾウ」は新規追加なので、そのまま5が設定されます。

このメソッドは、出現回数をカウントする処理でよく使われるので、覚えておきましょう。

Mapの初期化・まとめて生成する方法

Mapを作成する際、毎回put()で一つずつ追加するのは手間がかかります。
そんなとき、Java9以降ではMap.of()メソッドを使うことで、複数の要素をまとめて設定できるので便利です。

このメソッドで作成したMapは、後から要素を追加したり削除したりできません。設定ファイルのような、変更されては困るデータを扱う際に有用です。

また、既にあるリストなどのコレクションから、Mapを作りたい場合もあります。そうした際、Stream APIのCollectors.toMap()を使うと、既存のデータを元に新しいMapを自動生成できます。

import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { Map<String, String> immutableMap = Map.of( "ライオン", "百獣の王", "ゾウ", "巨体動物", "ウサギ", "俊敏動物" ); List<String> animals = Arrays.asList("ライオン", "ゾウ", "ウサギ"); Map<String, Integer> nameLength = animals.stream() .collect(Collectors.toMap(name -> name, String::length)); System.out.println("不変Map: " + immutableMap); System.out.println("名前の長さMap: " + nameLength); } }

出力結果(例)

不変Map: {ライオン=百獣の王, ゾウ=巨体動物, ウサギ=俊敏動物}
名前の長さMap: {ライオン=4, ゾウ=2, ウサギ=3}

Map.of()で作成したMapは不変という特徴があるので、後から要素を追加したり削除したりすることはできません。定数として使いたい場合に適しています。

2つ目の例では、動物名のリストから「名前をキー、文字数を値」とするMapを自動生成しています。

よくある質問(Q&A)

Q: HashMapとTreeMapはどう使い分ければよいですか?

A: パフォーマンスを重視し順序が不要ならHashMap、キーを常にソートした状態で保持したいならTreeMapを選択しましょう。HashMapは高速ですが順序は保証されません。TreeMapは自動ソートされますが、挿入・検索に少し時間がかかるという特徴があります。

Map<String, Integer> hashMap = new HashMap<>(); // 高速 Map<String, Integer> treeMap = new TreeMap<>(); // ソート済み

Q: Mapでnullは値として使用できますか?

A: 基本的に、どのMapでも値としてnullを使用することができます。キーについては、HashMapとLinkedHashMapは1つだけnullキーを許可し、TreeMapはnullキーを許可しません。

HashMap<String, String> map = new HashMap<>(); map.put(null, "nullキー"); // OK map.put("key", null); // OK

クラス

キーに null

値に null

HashMap

1つだけ許可

複数許可

LinkedHashMap

1つだけ許可

複数許可

TreeMap

許可しない(例外発生)

複数許可

Q: Mapのサイズを事前に指定すべきですか?

A: 要素数が事前にわかっている場合は、初期容量を指定することで性能向上が期待できます。HashMapは容量不足になると内部的に再構築が発生するため、適切な初期容量を設定することで効率が向上します。

Map<String, String> map = new HashMap<>(16);  // 初期容量16

Q: 複数のMapを結合するにはどうすればよいですか?

A: putAll()メソッドで別のMapの全要素を追加できます。Java8以降はStream APIを使って複数のMapを結合することも可能です。同じキーが存在する場合は、後から追加された値で上書きされます。

Map<String, String> map1 = new HashMap<>(); Map<String, String> map2 = new HashMap<>(); map1.putAll(map2); // map2の内容をmap1に結合

Q: Mapの要素をループ中に削除しても安全ですか?

A: 拡張for文を使ってMapをループ処理している最中に、要素を削除しようとすると問題が発生する可能性があります。
これは、ループ中にMapの構造が変わることで、プログラムが混乱してしまうためです。

安全に削除するにはIteratorのremove()メソッドを使用するか、削除対象のキーを先に収集してからremoveAll()でまとめて削除しましょう。

Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator(); while (it.hasNext()) { if (条件) it.remove(); // 安全な削除 }

まとめ

JavaのMapは、キーと値のペアでデータを効率的に管理できる基本的なデータ構造です。

この記事では、Mapの基本概念から実装クラスの違い、基本操作、繰り返し処理、ソート方法、そして応用テクニックまで解説してきました。

Mapが特に活躍するのは、次のような場面です。

Mapが活躍する場面

  • キーを使って特定のデータを高速に検索・取得したいとき
  • 辞書のような「名前と値」のペアでデータを管理したいとき
  • 出現回数のカウントや集計処理を行いたいとき
  • 設定情報や変換テーブルを効率的に保持したいとき

最後に、重要なポイントをおさらいしましょう。

重要なポイント

  • キーと値のペア構造により高速なデータ検索ができる
  • HashMap、TreeMap、LinkedHashMapの特徴
  • put()、get()、remove()などの基本操作
  • 拡張for文やStream APIを活用すると効率的に繰り返し処理ができる
  • putIfAbsent()やmerge()などの応用メソッド

Mapの使い方を理解することで、データ処理の幅が大きく広がります。

特に、最初のうちは基本的なHashMapを使って、put()やget()といった基本操作に慣れることが大切です。
慣れてきたら、順序を保持したい場合はLinkedHashMap、自動ソートが必要な場合はTreeMapというように、用途に応じて使い分けてみてください。

また、キーと値のペアという仕組みは、プログラミングの様々な場面で活用できる重要な概念です。
この記事で学んだ知識をしっかり身に付けることで、より実用的で効率的なJavaプログラムを作れるようになるでしょう。

レベルを更に上げたい方はpaizaプログラミングスキルチェックへ

  1. paizaラーニングトップ
  2. ナレッジ
  3. Javaのナレッジ記事一覧
  4. JavaのMapをマスターしよう!基本概念から実装まで初心者向けに解説