Map(連想配列)
Mapはキーと値を結びつけるデータ構造です。
例えば、「baseball」に対して「野球」、「soccer」に対して「サッカー」を結びつけるようなイメージです。
先述のListや配列では、値は一つしかありませんが、こちらは2つで1セットになるイメージです。
値を格納して自動的に添字の番号(0,1,2…)が割り振られるのとは異なり、番号をキーとして名前をつけることができる配列を連想配列といいます。
添字番号にキーとして名前をつけることにより、そのキーを指定して値を取り出すなどの管理がしやすくなるといったメリットがあります。
Mapの記載方法
Mapは以下のように記載することで使用できます。
Map <キーのデータ型名 , 値のデータ型名> 変数名 = new HashMap <キーのデータ型名 , 値のデータ型名> ();
Listでは、<>内に値のデータ型を入れていましたが、Mapではそれに加えてキーのデータ型も必要とします。
Mapへの値の追加
Listでは「.add()」を使用して値の追加を行いました。Mapでは追加するデータが2つに増えることから「.put()」を使用し以下のように記述します。なお、List同様、後から追加することが可能です。
Map変数名.put (キーにする値 , キーと連動する値);
Mapの値の取得
List同様「.get()」を使用します。Listではインデックス番号を指定して出力をしていましたが、Mapではキーを指定して出力することが出来ます。
Map変数名.get(キー);
キーが存在しない場合、nullが返されます。
Mapの値の変更
Listの時は「.set()」を使い、書き換えを行っていましたが、Mapを使った書き換えには「replace()」を使用します。
Map変数名.replace(変更対象キー,変更内容の値);
Mapの値の削除
List同様、「.remove()」を使用します。指定するものがキーという点が異なります。
Map変数名.remove(キー);
これらのメソッドを用いてMapを操作してみましょう。
「com.cmps.map」パッケージを作成し、その中に「MapManual」クラスを作成してください。
mainメソッド内に以下のソースを記述しましょう。
// Mapの宣言
Map<String, String> testMap = new HashMap<String, String>();
// 値の追加
testMap.put("Apple", "りんご");
testMap.put("Grape", "ぶどう");
testMap.put("Banana", "バナナ");
// Mapの中身を確認
System.out.println(testMap);
// 値の出力
String str = testMap.get("Grape");
System.out.println(str);
// 値の変更
testMap.replace("Grape", "葡萄");
// 変更を確認
System.out.println(testMap);
// 値の削除
testMap.remove("Grape");
// 変更を確認
str = testMap.get("Grape");
System.out.println(str);
Mapの種類
Mapには、今回使用した「HashMap」の他にTreeMapやLinkedHashMapといった種類があります。
それぞれの特徴は以下の通りです。
| クラス(Map) | 概要 |
| HashMap | ハッシュを使ったMapのデフォルト。要素数によらない高速な検索ができます。 順序が保持されていないので、目的の要素のキーを指定してそれに紐づく値を取得するのが早い。 |
| TreeMap | ツリー構造を持つMapクラスで順序がキーの順番になっています。 キーが数値の場合は小さい順に要素が保持されます、 |
| LinkedHashMap | キーの挿入順を保持します。コンストラクタの引数の指定によって、 挿入順ではなくアクセス順を保持することもできます。デフォルトは挿入順です。 または、コンストラクタの引数に順序付けモードを指定して、アクセス順に保持されるようにすることもできます。 |
これらのMapについても操作方法には大きく差がありませんが、用途用途に合わせて使い分けが必要になる場面も出てきます。
forEachメソッド(ListやMapで使用できる繰り返し処理)
Javaの繰り返し処理にはfor文、拡張for文などがありましたが、forEachメソッドというものもあります。
forEachメソッドはfor文と同様に配列、List、Mapなどに対して繰り返し処理を行うときに使います。
forEachメソッドは次のような形式で記述します。
下記の引数が取り出した1つ1つの要素を受け取る変数名になります。
[配列名・コレクション名(Listなどの変数名)].forEach(引数 -> 繰り返し行う処理(引数))
実際に記述して確認してみましょう。
// forEachメソッド確認用のList
List<String> forEachList = new ArrayList<String>();
forEachList.add("あいう");
forEachList.add("かきく");
forEachList.add("さしす");
// forEachメソッド
forEachList.forEach(s -> System.out.println(s));
forEachメソッドに出てくる「->」はラムダ式の文法です。
ラムダ式とは、関数型インターフェースを実装したクラスのインスタンスを、
短いコーディング量で簡単に作れてしまう文法のことです。
ラムダ式について詳しくは以下のサイトを参照してください。
Javaのラムダ式とは?ラムダ式のメリットや使い方を解説
forEachメソッドでは要素の型の定義が不要となり、省略してコードを書くことができます。
上記の例を見て頂ければわかるように、forEachメソッドとラムダ式を使用することで、
ループ処理を1行で記述することが可能となります。
配列やMapでは以下のように記述します。
// 配列
int[] forEachArray = {1,10,100,100};
Arrays.stream(forEachArray).forEach(s->System.out.println(s));
// Map
Map<String, String> forEachMap = new HashMap<String, String>();
forEachMap.put("apple", "りんご");
forEachMap.put("banana", "バナナ");
forEachMap.put("orange", "オレンジ");
forEachMap.forEach((Key,value) -> System.out.println(Key + ":" + value));
固定長配列でforEachメソッドを使う場合、記述はほぼ同じですがStreamAPIも一緒に使う必要があります。
Mapの記述例では、キー(key)と値(value)の両方を出力しています。
Mapに対してforeachメソッドを使用する場合、(Key,value) のようにラムダ式の左辺に、第一引数をキー、第二引数を値として記述する必要があります。
インデックス番号を取得する方法
forEachメソッドには繰り返し処理の回数を数える変数(ループカウンタ)が無いため、
for文のようにインデックス番号を取得することは来ません。
indexOfメソッド
インデックス番号を取得する方法の一つとして、indexOfメソッドがあります。
indexOfメソッドは引数の値と同じ要素のインデックス番号を返すので、
これを使ってインデックス番号を取得することができます。
// インデックス番号を取得
List<String> indexList = new ArrayList<String>();
indexList.add("ねずみ");
indexList.add("うし");
indexList.add("とら");
indexList.add("うし");
indexList.add("うま");
indexList.forEach(detail -> System.out.println(indexList.indexOf(detail) + ":" + detail));
出力結果を見てみると
0:ねずみ
1:うし
2:とら
1:うし
4:うま
4番目の「うし」のインデックス番号が「3」ではなく「1」になっています。
indexOfメソッドでは同じ要素が複数含まれる場合は初めのインデックス番号が表示されるので注意しましょう
ループカウンタとして使う変数をループの外に用意する
ラムダ式のループの中からは、外側で宣言した変数に値に代入することができませんが、
配列にした場合は値を変更することができます。
indexOfメソッドで作成したArrayList配列(indexList)に対して、以下の処理を記述してみましょう。
なお、ラムダ式に対応する処理が複数行ある場合、以下のようにブロック{}で囲むことで複数行を記載可能です。
int[] i = {0}; // 配列の初期化(最初の要素に0を設定)
indexList.forEach(loop -> {
System.out.println(i[0] + ":" + loop);
i[0]++; // 配列「i」の要素をインクリメント
});
出力結果は以下の通りです。
0:ねずみ
1:うし
2:とら
3:うし
4:うま
ループカウンタとして使う変数(ここでは「i」)をループの外に用意することで、インデックス番号が取得できましたね!
練習問題
「com.cmps.map」パッケージ内に「MapQuestion」クラスを作成してください。
問1: 適当な値が3個格納された連想配列を作成して、全て値を表示しましょう。
キーは値と関連する項目を設定してキーも表示しましょう。
問2:以下の表は国内のじゃがいも生産量の上位5位を掲載したものです。
順位をキーとし値に県名が格納された配列を作成して三番目を表示してください。
| 順位 | 都道府県 |
| 1 | 北海道 |
| 2 | 長崎県 |
| 3 | 鹿児島県 |
| 4 | 茨城県 |
| 5 | 千葉県 |
問3: オレンジ・リンゴ・バナナの各情報(値段・在庫数・原産地)が格納された多次元連想配列を作成して、
リンゴの原産地を表示してください。
問4: 問3で作成した多次元連想配列の各情報を表示しましょう。
値は適当に設定し、表示する際はキーと値を表示してください。例:値段は100円
問5: 人ごとに下記の情報が格納された多次元連想配列を定義してください。項目名もキーとして用い、人ごとの配列のキーは数値(0,1,2)としてください。
作成した配列に対し、条件に合致する場合は値を変更しましょう。
| 名前 | 性別 | 年齢 | 職業 |
|---|---|---|---|
| 田中 | 男 | 25 | IT |
| 鈴木 | 女 | 38 | 運転手 |
| 佐藤 | 男 | 18 | 学生 |
1.年齢が20歳以上の場合、性別を「男」に変更しましょう。ただし既に「男」の場合は「女」に変更してください。
2.職業が「学生」の場合、新たに「身長」という項目を追加して175と設定してください。それ以外の場合は165に設定してください。
3.forEachメソッドを使い中身を表示しましょう。
練習問題のヒント
問1
拡張for文または、forEachメソッドを使い解きましょう。
問2
順序がキーとなるので、TreeMapを使用し配列を作成してみましょう。
問3
オレンジ・リンゴ・バナナのそれぞれの連想配列を作成し、
その配列を一つの連想配列に.put()を使い組み込みましょう。
なお組み込み先の連想配列では、「オレンジ」「リンゴ」「バナナ」がそれぞれキーとなります。
問4
forEachメソッドのネスト(forEachメソッドの中でもう一度forEachメソッドを使用)を使用し、
キーと値を取り出します。
問5
問3と同様の方法で配列を作成します。
ただ配列を作成するにあたり、数値も文字列で配列に追加することになるので、
Integer.parseInt()を用いて数値に変換してから、if文で条件分岐します。
