3

重複の可能性:
Java では、なぜ equals() と hashCode() が一貫していなければならないのですか?

equals() をオーバーライドするときは、常に hascode() を使用する必要があることを読みました。

そうでなければ、なぜそれが間違っているのか、実際の例を挙げてくれる人はいますか? つまり、hashCode() ではなく equals() をオーバーライドするときに発生する可能性がある問題です。

equals() をオーバーライドするたびに、堅牢な hasCode() 関数を記述する必要がありますか? それとも簡単な実装で十分ですか?

例えば、

以下のような不十分な実装でも、equals() と hashCode()? の間の契約を満たすには十分です。

public int hashCode() {
   return 91;
}
4

5 に答える 5

4

equalsとはどちらもhashcode、オブジェクトの単一性の原則に基づいています。equals返される場合true、両方のオブジェクトのハッシュコードが同じである必要があります。そうでない場合、ハッシュベースの構造とアルゴリズムは未定義の結果になる可能性があります。

などのハッシュベースの構造を考えてみてくださいHashMaphashcodeではなく、キーの参照を取得するためのベースとして呼び出されるためequals、ほとんどの場合、キーを見つけることができなくなります。また、 の不適切な実装は、パフォーマンスに影響を与えるhashcode衝突 (同じ を持つ複数のオブジェクトhashcode、どれが「正しい」オブジェクトか?) を作成します。

私見ですが、 (両方をオーバーライドするのではなく) equalsORhashcodeをオーバーライドすることは、コードの臭い、または少なくとも潜在的なバグの原因と見なす必要があります。つまり、遅かれ早かれコードに影響を与えないと 100% 確信している場合を除きます (いずれにせよ、その確信が持てるのはいつでしょうか?)。

注: および ビルダーを持つApache Commonsequalsのように、およびhashcodeビルダーを持つことによってこれをサポートするさまざまなライブラリがあります。HashcodeBuilderEqualsBuilder

于 2012-12-10T16:07:40.680 に答える
2

equals()およびは、やhashCode()などの特定のコレクションで組み合わせて使用​​されるため、これらのコレクションを使用する場合は、コントラクトに従ってオーバーライドすることを確認する必要があります。HashSetHashMaphashCode

オーバーライドしないと、とでhashCode問題が発生します。特に、「等しい」2つのオブジェクトは、等しくなければならない場合でも、異なるハッシュバケットに配置される可能性があります。HashSetHashMap

オーバーライドするhashCodeが、それが不十分な場合、パフォーマンスの問題が発生します。HashSetとのすべてのエントリHashMapは同じバケットに入れられ、O(1)のパフォーマンスが失われ、代わりにO(n)が使用されます。これは、データ構造が本質的に線形チェックされたリンクリストになるためです。

これらの条件の外でプログラムを壊すことに関しては、そうは思われませんが、API(特にサードパーティライブラリ)がいつこの契約に依存するかはわかりません。どちらも実装していないオブジェクトについては契約が守られているため、ライブラリはハッシュバケットを使用せずにどこかでこれに依存している可能性があります。

いずれにせよ、hashCode特にIDEを使用している場合は、良いものを実装するのは簡単です。EclipseとNetbeansはどちらも、(のアサーション)の逆ルールを含め、すべてのコントラクトに従う方法で生成する機能equalsを備えています。あなたがする必要があるのはあなたが含まれたいフィールドを選択して行くことです。hashCodeequalsa.equals(b) == b.equals(a)

于 2012-12-10T16:06:11.697 に答える
2

次のコードは、hashCode() を実装しないことで発生する可能性のあるバグを示しています。 Set.contains() は、最初にオブジェクトの hashCode() をチェックし、次に .equals() をチェックします。したがって、両方を実装しないと、.contains() は直感的に動作しません。

public class ContainsProblem {

// define a class that implements equals, without implementing hashcode
class Car {
    private String name;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Car)) return false;
        Car car = (Car) o;
        if (name != null ? !name.equals(car.name) : car.name != null) return false;
        return true;
    }

    public String getName() {return name;}

    public Car(String name) { this.name = name;}
}

public static void main(String[] args) {
    ContainsProblem oc = new ContainsProblem();

    ContainsProblem.Car ford = oc.new Car("ford");
    ContainsProblem.Car chevy = oc.new Car("chevy");
    ContainsProblem.Car anotherFord = oc.new Car("ford");
    Set cars = Sets.newHashSet(ford,chevy);

    // if the set of cars contains a ford, a ford is equal to another ford, shouldn't
    // the set return the same thing for both fords? without hashCode(), it won't:
    if (cars.contains(ford) && ford.equals(anotherFord) && !cars.contains(anotherFord)) {
        System.out.println("oh noes, why don't we have a ford? isn't this a bug?");
    }
}
}
于 2012-12-10T16:29:36.280 に答える
1

あなたの簡単な実装は正しいですが、ハッシュベースのコレクションのパフォーマンスが低下します。

Objectクラスの 2 つの異なるインスタンスが等しい場合、デフォルトの実装 (によって提供される) は契約を破ります。

于 2012-12-10T16:08:53.223 に答える
0

Joshua Bloch の「Effective Java」の第 3 章「すべてのオブジェクトに共通するメソッド」を読むことをお勧めします。彼ほどうまく説明できる人はいない. He 彼は、多数の Java プラットフォーム機能の設計と実装を主導しました。

于 2012-12-10T16:12:07.507 に答える