8
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaのMapで複合キーを使用する。

Last updated at Posted at 2017-02-11

概要

JavaのMapで複合キーを使うには、Mapのキーに、複合キーで使いたいフィールドを全て保持するクラスを指定する。
このとき、キーに使うクラスの以下のメソッドをオーバーライドする。

  • equals()
  • hashCode()

手順

  1. 複合キーに使いたいフィールドを保持するクラスを作成する。
  2. このクラスが暗黙的に継承するObjectクラスのメソッドequalsとhashCodeをオーバーライドする。
  3. 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を使う場合の手順>

  1. パッケージ・エクスプローラを開き、複合キーをフィールドに持つクラスを選択する。
  2. 画面上部のメニュー > ソース(S) > 「hashCode()およびequals()の生成(H)...」をクリック
  3. ダイアログ「hashCode()およびequals()の生成」が表示されるので、O.K.を押す。
  4. 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
8
9
1

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
8
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?