【Apex】Mapのキーとしてクラスを使用する

selective photography of skeleton key hanging Apex
Photo by Sergij on Pexels.com

Apex の Map ではクラスをキーとして扱うことができますが、その場合は注意が必要です。
この記事ではクラスをキーとして扱う方法について記述しました。

また、クラスを Set の要素として扱う際も同様の考え方が使えます。

実装例

public class KeyGroup {
    Date keyDate;
    String keyword;

    public KeyGroup(Date keyDate, String keyword) {
        this.keyDate = keyDate;
        this.keyword = keyword;
    }

    Boolean equals(Object obj){
        if(obj instanceof KeyGroup){
            KeyGroup kg = (KeyGroup) obj;
            return this.keyDate.isSameDay(kg.keyDate) && this.keyword == kg.keyword;
        }
        return false;
    }

    Integer hashCode(){
        return keyDate.year() + keyDate.month() + keyword.hashCode();
    }
}
Map<KeyGroup, Integer> sampleMap = new Map<KeyGroup, Integer>();
KeyGroup kg1 = new KeyGroup(Date.newInstance(2023, 1, 3), 'hoge');
KeyGroup kg2 = new KeyGroup(Date.newInstance(2024, 2, 4), 'fuga');
KeyGroup kg3 = new KeyGroup(Date.newInstance(2023, 1, 3), 'hoge');
sampleMap.put(kg1, 1);
sampleMap.put(kg2, 2);
System.debug(sampleMap.get(kg3));  // 1 が返される

ポイント

キーとして使用したいクラスに、equals メソッドと hashCode メソッドを実装(オーバーライド)します。

equals メソッドは次の条件を満たす必要があります。

x、y、z が null 以外のインスタンスであるとして、

  • 反射性: x.equals(x)
  • 対称性: x.equals(y) は、y.equals(x) が true を返す場合にのみ true を返す
  • 推移性: x.equals(y) が true を返し、かつ y.equals(z) が true を返す場合、x.equals(z) は true を返す
  • 整合性: x.equals(y) の複数の呼び出しで常に true を返すか常に false を返す
  • null 以外の参照値 x では、x.equals(null) は false を返す

hashCode メソッドは次の条件を満たす必要があります。

  • hashCode メソッドが Apex 要求の実行中に同じオブジェクトで複数回呼び出された場合、同じ値を返す必要がある。
  • equals メソッドで 2 つのオブジェクトが等価とされた場合、hashCode は同じ値を返す必要がある。
  • equals メソッドで 2 つのオブジェクトが等価でないとされた場合、hashCode は異なる値を返す必要はない。

なぜ equals と hashCode がどちらも必要なのか

get 等でMapのキーの中から一致するキーを探す場合、

  1. hashCode の値を比較
  2. 上記が一致していれば、equals で一致を判定

という順で処理が行われます。

hashCode (Integer) 同士の比較は equals を実行するよりも処理が軽いので、すべてのキーに対し equals を実行して比較するよりも全体的な処理は速くなるからです。

コメント

タイトルとURLをコピーしました