Javaのコレクション「Map」とは?便利な使い方も紹介!
Mapとは?
ざっくりいうと、2つの値が紐づいた関係を複数保持するデータのことです。
「社員番号の管理」的なイメージです。
社員番号がキー、名前が値、というのがセットで
そのセットが社員分あります。
名前やが同じ人がたまにいますが、
社員番号は必ず同じ番号がないのはわかると思います。
だいたいそんな感じです。
Mapの種類
実はMapとはあくまで入れ物のようなもの(インターフェース)なだけで、
実際に動作する実物は以下の3種類あります。
HashMap
中身の順番は保持されません。
そのため、順番を保持しないといけないデータには、使用できません。
LinkedHashMap
追加した順番に保持されます。
また、指定すればアクセス順にすることも可能です。
TreeMap
キーによって、自動的に並び替えされます。
主にキーが番号順・アルファベット順等の場合、
勝手に並び替えてくれるので便利です。
宣言する型について
一般的に下記のような宣言を見ることが多いかと思います。
Map<String, String> 変数名 = new HashMap<>();
このように、Mapの中に実物を入れていることが多いと思います。
ただ同じ型に入れることももちろん可能です。
HashMap<String, String> 変数名 = new HashMap<>();
どう違うのかと言うと、使える機能が違います。
細かくいえば他にも違いはありますが、基本的にはMapのインターフェースに格納し、
必要に応じてインターフェースに無い機能を使いたい時だけHashMap等を使用するイメージで大丈夫だと思います。
そのため以下は全てMapに格納しております。
基本的な使い方
前提として、Mapの中身のことは「要素」と呼びます。
初期化する
Mapオブジェクトを初期化(生成)します。
基本的に引数は指定しなくてもいいですが、「初期容量」と「負荷係数」を指定できます。
指定しない場合、デフォルトで初期容量が16、負荷係数が0.75となります。
何かというと、オブジェクト生成時にまず、初期容量分の保存領域を確保します。
そして要素を追加していく中で、初期容量の負荷係数個に到達すると、保存領域を拡張します。
上記例でいうと、16個の領域があって、係数が0.75ということは、12個の要素を追加するときに、拡張します。
その後も負荷係数に達するたびに拡張し続けていきます。
この要領を拡張するという動作が非常に重たい動作になるので、できる限り最適に設定していきたいので、
最初からある程度量が分かっているのであれば、指定してあげるとパフォーマンスがよくなります。
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// デフォルトの初期容量16とデフォルトの負荷係数0.75のインスタンスを生成
Map<String, String> nameMap1 = new HashMap<>();
// 初期容量10とデフォルトの負荷係数0.75のインスタンスを生成
Map<String, String> nameMap2 = new HashMap<>(10);
// 初期容量5と負荷係数1.0のインスタンスを生成
Map<String, String> nameMap3 = new HashMap<>(5, 1.0f);
}
}
要素を追加する
キーと値を紐づけて、登録していきます。
例でいうと、社員番号をキー、フルネームを値、として紐づけて登録しています。
すでに登録済みのキーで追加すると、値が上書きされます。
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// インスタンスを生成
Map<String, String> nameMap = new HashMap<>();
// 追加 {22-01=田中}
nameMap.put("22-01", "田中");
// 追加 {22-01=佐藤, 22-02=鈴木}
nameMap.put("22-02", "鈴木");
// 追加 {22-01=佐藤, 22-02=鈴木}
nameMap.put("22-01", "佐藤");
}
}
要素を削除する
キーのみを指定して削除するか、キーと値の組み合わせを指定して、削除する方法があります。
キーのみを指定した場合は、キーが存在する時だけ、要素を削除します。
キーと値を指定した場合は、その組み合わせが存在する時だけ、要素を削除します。
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// インスタンスを生成
Map<String, String> nameMap = new HashMap<>();
// 追加 {22-01=佐藤}
nameMap.put("22-01", "佐藤");
// 追加 {22-01=佐藤, 22-02=鈴木}
nameMap.put("22-02", "鈴木");
// 追加 {22-01=佐藤, 22-02=鈴木, 22-03=田中}
nameMap.put("22-03", "田中");
// キーが一致する要素があれば削除
// {22-02=鈴木, 22-03=田中}
nameMap.remove("22-01");
// キーと値が一致する要素があれば削除
// {22-03=田中}
nameMap.remove("22-02", "鈴木");
}
}
要素を取得する
キーを指定して値を取得します。
キーが存在しない場合は、nullを返します。
また、「getOrDefault」を使用すると、もしキーが存在しない場合、
返却値をnullではなく第二引数にすることができます。
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// インスタンスを生成
Map<String, String> nameMap = new HashMap<>();
// 追加 {22-01=佐藤}
nameMap.put("22-01", "佐藤");
// 追加 {22-01=佐藤, 22-02=鈴木}
nameMap.put("22-02", "鈴木");
// 追加 {22-01=佐藤, 22-02=鈴木, 22-03=田中}
nameMap.put("22-03", "田中");
// 値を取得 > 佐藤
nameMap.get("22-01");
// 値を取得 > null
nameMap.get("0000");
// 値を取得 > 佐藤
nameMap.getOrDefault("22-01", "特定不可");
// 値を取得 > 特定不可
nameMap.getOrDefault("0000", "特定不可");
}
}
要素を変更する
要素の値を変更します。
キーと変更後の値を指定すれば、キーが存在するときだけ、変更後の値に変更します。
キーと変更前の値と変更後の値を指定すれば、
キーと変更前の値の組み合わせが存在する場合のみ、変更後の値に変更します。
上記に出てきた「put」の上書きとの違いは、キーが存在する場合は一緒ですが、
「put」は、キーが存在しない場合、そのまま追加されます。
「replace」は、キーが存在しない場合、何も起きません。
こういう違いがあるので、値を変更する場合は使い分けしましょう。
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// インスタンスを生成
Map<String, String> nameMap = new HashMap<>();
// 追加 {22-01=佐藤}
nameMap.put("22-01", "佐藤");
// 追加 {22-01=佐藤, 22-02=鈴木}
nameMap.put("22-02", "鈴木");
// キーが一致する要素があれば値を置換
// {22-01=田中, 22-02=鈴木}
nameMap.replace("22-01", "田中");
// キーと値が一致する要素があれば値を置換
// {22-01=佐藤, 22-02=鈴木}
nameMap.replace("22-01", "田中", "佐藤");
}
}
要素の数を取得する
Map内の要素の数を取得します。
取得できる数は人間が数える数字と同じなので、
1個、2個と数えるやり方と同じです。
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// インスタンスを生成
Map<String, String> nameMap = new HashMap<>();
// 中身の数を取得 > 0
nameMap.size();
// 追加 {22-01=佐藤}
nameMap.put("22-01", "佐藤");
// 中身の数を取得 > 1
nameMap.size();
// 追加 {22-01=佐藤, 22-02=鈴木}
nameMap.put("22-02", "鈴木");
// 中身の数を取得 > 2
nameMap.size();
}
}
要素があるか判定する
Mapに要素があるかどうかを判定します。
ただし完全一致しないと存在しないと判定されます。
キーが存在するかの判定と、値が存在するかの判定、別々に用意されています。
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// インスタンスを生成
Map<String, String> nameMap = new HashMap<>();
// 追加 {22-01=佐藤}
nameMap.put("22-01", "佐藤");
// 追加 {22-01=佐藤, 22-02=鈴木}
nameMap.put("22-02", "鈴木");
// 追加 {22-01=佐藤, 22-02=鈴木, 22-03=田中}
nameMap.put("22-03", "田中");
// キーが存在するかを判定
if (nameMap.containsKey("22-01")) {
// 今回は存在するのでtrue
}
// 値が存在するかを判定
if (nameMap.containsValue("佐藤")) {
// 今回は存在するのでtrue
}
}
}
要素をすべてループする
Map内のすべてに何かを行いたいときは、要素をすべてループします。
キーだけをすべて取得する「keySet()」、値だけをすべて取得する「values()」というのもありますが、
実は今回紹介する「entrySet()」のほうが処理速度が速いので、基本はentrySetを使用したほうがいいと思います。
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// インスタンスを生成
Map<String, String> nameMap = new HashMap<>();
// 追加 {22-01=佐藤}
nameMap.put("22-01", "佐藤");
// 追加 {22-01=佐藤, 22-02=鈴木}
nameMap.put("22-02", "鈴木");
// 追加 {22-01=佐藤, 22-02=鈴木, 22-03=田中}
nameMap.put("22-03", "田中");
// Map内をすべてをループ
for (Entry<String, String> entryName : nameMap.entrySet()) {
// キーを取得
entryName.getKey();
// 値を取得
entryName.getValue();
}
}
}
便利な使い方
並び替え(ソート)する
上記で紹介した通り、並び替えを行うのであれば、TreeMapを使用します。
キーで自動的に並び替えが行われます。
デフォルトは昇順のため、逆順に指定することで、降順に並び替えもできます。
ただ、自動のため少し特殊な並び替えは自分で実装しないといけません。
そういう場合は判定したうえで、LinkedHashMapに順に追加するのが楽でしょう。
import java.util.Collections;
import java.util.TreeMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// 昇順の自動ソートMapのインスタンスを生成
Map<String, String> autoSortMap = new TreeMap<>();
// 自動ソートMapに追加 {01=佐藤, 02=鈴木, 03=田中}
autoSortMap.put("03", "田中");
autoSortMap.put("01", "佐藤");
autoSortMap.put("02", "鈴木");
// 降順の自動ソートMapのインスタンスを生成 {03=田中, 02=鈴木, 01=佐藤}
Map<String, String> autoReverseSortMap = new TreeMap<>(Collections.reverseOrder());
// 自動ソートMapに追加
autoReverseSortMap.put("03", "田中");
autoReverseSortMap.put("01", "佐藤");
autoReverseSortMap.put("02", "鈴木");
}
}
複数のキーを指定する
複数のキーを指定して値を取得したいことは、案外多い気がします。
ですが、Mapでは一つしかキーを指定できません。
Mapの中にMapを入れてしまう方法もありますが、キーに疑似的に複数のキーを指定するのが楽だと思います。
絶対にキーに含まれることのないと断言できる文字列を、区切り文字として指定して、
連結して一つのキーとして追加します。
例でいうと、部署名と社員番号をキーとしたいため、
「社員番号 + 区切り文字 + 社員番号」をキーとしています。
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// 区切り文字を指定
String splitStr = "|";
// インスタンスを生成
Map<String, String> nameMap = new HashMap<>();
// 部署名と社員番号で複数キーを疑似的に追加
nameMap.put("営業部" + splitStr + "0001", "佐藤");
nameMap.put("営業部" + splitStr + "0002", "鈴木");
nameMap.put("人事部" + splitStr + "0001", "田中");
// 複数キーで取得 > 佐藤
nameMap.get("営業部" + splitStr + "0001");
}
}
まとめ
Javaのコレクションの「Map」の紹介でした。
Mapに限らずにJavaのコレクションを使いこなせれば、
楽に処理を実装できますし、わかりやすく書けますので、WinWinです。
使いこなせれば世界が変わるといっても過言ではないと思います!
以上、ここまで見ていただきありがとうございます。
皆さまの快適な開発ライフに、ほんの少しでもお役に立てれば幸いです。