概要
JavaのMapで複合キーを使うには、Mapのキーに、複合キーで使いたいフィールドを全て保持するクラスを指定する。
このとき、キーに使うクラスの以下のメソッドをオーバーライドする。
- equals()
- hashCode()
手順
- 複合キーに使いたいフィールドを保持するクラスを作成する。
- このクラスが暗黙的に継承するObjectクラスのメソッドequalsとhashCodeをオーバーライドする。
- Mapクラスのキーに作成したクラスを指定する。
例
以下のような売上金を保持するクラスを考え、このクラスを複合キーを使ったMapで管理してみる。
- 売上金クラスのフィールド
-
・店舗番号
・売上日
・売上金額
- 複合キーに使うフィールド
-
・店舗番号
・売上日
① 複合キーに使いたいフィールドを保持するクラスを作成する。
複合キークラス
package hoge;
import java.time.LocalDate;
/** 複合キー */
public class CompositeKey {
/** 店舗番号 */
private final Integer shopNo;
/** 売上日 */
private final LocalDate salesDate;
public CompositeKey(Integer storeNo, LocalDate salesDate) {
this.shopNo = storeNo;
this.salesDate = salesDate;
}
}
② そのクラスのequalsメソッドとhashCodeメソッドをオーバーライドする。
<Eclipseを使う場合の手順>
- パッケージ・エクスプローラを開き、複合キーをフィールドに持つクラスを選択する。
- 画面上部のメニュー > ソース(S) > 「hashCode()およびequals()の生成(H)...」をクリック
- ダイアログ「hashCode()およびequals()の生成」が表示されるので、O.K.を押す。
- equalsメソッドとhashCodeメソッドが複合キーのクラスに挿入される。
equalsメソッドとhashCodeメソッドは、すべてのクラスの継承元となるObjectクラスに定義されているため、この操作は、Objectクラスのメソッドをオーバーライドする事になる。
○ オーバーライドする理由
- ・equalsメソッド
- オーバーライドすることにより、全てのフィールドが一致する場合のみ、同じオブジェクトとして判定されるようになる。 オーバーライドしないと、フィールドの中身で比較されないので、複合キーとして使えない。
- ・hashCodeメソッド
- オーバーライドすることによって、各フィールドの値を元にHash値が生成されるようになる。
equals()およびhashCode()を追加した複合キークラス
複合キークラス
package hoge;
import java.time.LocalDate;
/** 複合キー */
package hoge;
import java.time.LocalDate;
/** 複合キー */
public class CompositeKey {
/** 店舗番号 */
private final Integer shopNo;
/** 売上日 */
private final LocalDate salesDate;
public CompositeKey(Integer storeNo, LocalDate salesDate) {
this.shopNo = storeNo;
this.salesDate = salesDate;
}
/* (非 Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((salesDate == null) ? 0 : salesDate.hashCode());
result = prime * result + ((shopNo == null) ? 0 : shopNo.hashCode());
return result;
}
/* (非 Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CompositeKey other = (CompositeKey) obj;
if (salesDate == null) {
if (other.salesDate != null)
return false;
} else if (!salesDate.equals(other.salesDate))
return false;
if (shopNo == null) {
if (other.shopNo != null)
return false;
} else if (!shopNo.equals(other.shopNo))
return false;
return true;
}
}
③ 動作確認
<テスト・シナリオ>
HashMapに売上金データを格納し、複合キーで値を取得する。
○ 準備
1. テストデータを用意する。
- 売上金
店舗番号 | 売上日 | 売上金額 |
---|---|---|
1 | 2017/01/01 | 100 |
2 | 2017/01/02 | 200 |
3 | 2017/01/03 | 300 |
2. 売上金を保持するクラスを作成。
売上金クラス
package hoge;
import java.time.LocalDate;
/** 売上レコード */
public class SalesRecord {
/** 店舗番号 */
private final Integer shopNo;
/** 売上日 */
private final LocalDate salesDate;
/** 売上金額 */
private final Integer sales;
public SalesRecord(Integer storeNo, LocalDate salesDate, Integer sales) {
this.shopNo = storeNo;
this.salesDate = salesDate;
this.sales = sales;
}
public Integer getStoreNo() {
return shopNo;
}
public LocalDate getSalesDate() {
return salesDate;
}
public Integer getSales() {
return sales;
}
/** テスト用 */
@Override
public String toString() {
return "SalesRecord [storeNo=" + shopNo + ", salesDate=" + salesDate + ", sales=" + sales + "]";
}
}
3. テストを実行するクラスを作成。
Mainクラス
package hoge;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestMain {
public static void main(String[] args) {
// テストデータ
List<SalesRecord> salesRecords = new ArrayList<>();
salesRecords.add(new SalesRecord(1,LocalDate.of(2017,1,1),100));
salesRecords.add(new SalesRecord(2,LocalDate.of(2017,1,2),200));
salesRecords.add(new SalesRecord(3,LocalDate.of(2017,1,3),300));
// HashMapを生成
Map<CompositeKey,SalesRecord> map = new HashMap<>();
for (SalesRecord record : salesRecords) {
map.put(new CompositeKey(record.getStoreNo(), record.getSalesDate()), record);
}
// KeyからValueを取り出せることを確認する。
System.out.println(map.get(new CompositeKey(2,LocalDate.of(2017,1,2))));
}
}
○ テスト実施
HashMapに格納した売上金データから、以下の複合キーでレコードを取得してみる。
店舗番号 | 売上日 |
---|---|
2 | 2017/01/02 |
○ テスト結果
コンソール
SalesRecord [storeNo=2, salesDate=2017-01-02, sales=200]
指定したレコードを取り出せている事が確認できた。
サンプルコード
・GitHub
JavaのMapで複合キーを使用する
テストのために作成したコードは上記に格納した。
ただし、掲載したコードはMainクラスを含んでいるため、コピー&ペーストでも実行できる。
確認環境
- Windows10 Home
- Eclipse Neon.2 Release(4.6.2)
- JavaSE-1.8