web-dev-qa-db-ja.com

HashCodeBuilderおよびEqualsBuilderの使用スタイル

私は、Apache HashCodeBuilderとEqualsBuilderをリフレクションを使用したオブジェクトの平等のために使用することがよくありますが、最近、同僚が、エンティティに多くのプロパティが含まれる場合、リフレクションを使用するとパフォーマンスが大幅に低下する可能性があると教えてくれました。間違った実装を使用しているのではないかと心配ですが、私の質問は、次のアプローチのうちどれを好むでしょうか?なぜ?

public class Admin {

    private Long id;
    private String userName;

    public String getUserName() {
        return userName;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Admin)) {
            return false;
        }
        Admin otherAdmin  = (Admin) o;
        EqualsBuilder builder = new EqualsBuilder();
        builder.append(getUserName(), otherAdmin.getUserName());
        return builder.isEquals();
    }

    @Override
    public int hashCode() {
        HashCodeBuilder builder = new HashCodeBuilder();
        builder.append(getUserName());
        return builder.hashCode();
    }
}

Vs.

public class Admin {

    private Long id;
    private String userName;

    public String getUserName() {
        return userName;
    }

    @Override
    public boolean equals(Object o) {
      return EqualsBuilder.reflectionEquals(this, o, Arrays.asList(id));
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this, Arrays.asList(id));
    }
}
28
tintin

もちろん、2番目のオプションはよりエレガントでシンプルです。ただし、パフォーマンスが懸念される場合は、最初のアプローチを使用する必要があります。セキュリティマネージャーが実行されている場合、2番目の方法も失敗します。

私があなたの状況にあったなら、私は最初の選択肢に行きます。また、hashCodeを生成する最初のアプローチに誤りがあります。これは、builder.toHashCode()を返す必要があります。 return builder.hashCode();(ハッシュコードビルダーオブジェクトのハッシュコードを返す)の代わりに

19
ssk

私は2つの理由で2番目のオプションを好むでしょう:

  1. 明らかに読みやすい

  2. 関連するメトリックが含まれていない限り、最初のオプションのパフォーマンス引数を購入しません。例えば。リフレクションベースの「等しい」は、典型的なエンドツーエンドのリクエストレイテンシーに何ミリ秒追加しますか?全体として、それは何パーセントの増加でしょうか?最適化が時期尚早である可能性が高いことを知らずに

8

私はこれらのどちらも良い実装ではないと言います。 EqualsBuilderは、次の理由から使用するのに適したフレームワークではないと主張します。

  1. 拡張できません。あなたが平等を主張しようとしているフィールドの1つがヌルと空白を等しいものとして扱う必要がある場合はどうなりますか?
  2. 変数のリストは、ハードコーディングされた変数であるかのように維持する必要があります。つまり、比較するすべての変数をリストする必要があります。この時点で、a == o.getA()&& b == o.getB()の間に違いはありません...
  3. リフレクションを使用すると、指摘したとおり、および数十億のオブジェクトを破壊するエンタープライズアプリケーションで、追加のリソースが使用されます。このリフレクションを同等にすることは、メモリリークが発生するのと同じくらい悪いです。

Apacheのフレームワークよりも優れたフレームワークが必要だと思います。

3
Churk

書かれているあなたの質問は、2番目のアプローチの利点の1つを明確に示しています。

最初のケースでは、正しいreturn builder.hashCode()の代わりに、very間違いを犯しやすいreturn builder.toHashCode()、その結果、微妙なエラーが発生し、追跡が非常に困難になります。

2番目のケースは、このタイプミスの可能性を排除し、バグを見つけようとしてキーボードに頭をぶつけることを減らします。

3
Krease

@Churkに同意すると、Apache HashCodeBuilderとEqualsBuilderはうまく実装されていません。 HashCodeBuilderはまだ素数で遊んでいます!さらに、非常に多くの不必要な作業を行います。ソースを読みましたか?

Java 5(以前ではない場合)なので、AbstractHashMap <>は、ハッシュバケットを見つけるために素数のモジュロを使用していません。代わりに、バケットの数は2のべき乗と低次です。ハッシュコードのNビットは、バケットを見つけるために使用されます。

さらに、アプリケーションによって提供されたハッシュコードを「ミックス」して、ビットが均等に拡散し、バケットが均等に満たされるようにします。

したがって、int Object.hashCode()をオーバーライドする正しい方法は、クラスを使用してanyコレクションで共存するオブジェクトの母集団の中で最もアリティが高い最も単純な定数値を返すことです。

通常、ID値nmodifiedが最善の策です。 IDフィールドが整数の場合は、(int)にキャストして返します。文字列または他のオブジェクトの場合、そのハッシュコードを返すだけです。あなたはアイデアを得る。複合識別子の場合、最も明確な値を持つフィールド(またはそのhashCode)を返します。少ないほうがいいですね。

もちろん、hashCode()とequals()の間の契約は満たされなければなりません。したがって、それに応じてequals()を実装する必要があります。 hashCode()は平等に必要な完全な修飾子を使用する必要はありませんが、hashCode()で使用されるフィールドはequals()で使用されなければなりません。ここで、StringUtils.equals(s1、s2)のようなメソッドは、一貫して安全にnull値を処理するのに役立ちます。

2
Charlie