ホームページ >Java >&#&チュートリアル >Java で hashCode メソッドを実装するためのサンプル コードの共有

Java で hashCode メソッドを実装するためのサンプル コードの共有

黄舟
黄舟オリジナル
2017-03-28 10:47:521865ブラウズ

httpオブジェクト の唯一の識別子は、単に適切なイコールを書くだけでは達成できないことはご存知でしょう

それは素晴らしいことですが、ここで、

hashCode メソッドも実装する必要があります。

Equal と Hash Code

Equal を使用する理由と方法を見てみましょう。一般的に、ハッシュ コードがある場合はより効率的です。理解するのが難しいですが、実装の詳細だけでパフォーマンスが向上すると言えます

ほとんどのデータ構造は、equals メソッドによって要素が含まれているかどうかを判断します。例:

List<String> list = Arrays.asList("a", "b", "c");
boolean contains = list.contains("b");

This

変数

contains は true と評価されます。 b" は同じインスタンスではありません (さらに、文字列常駐を無視します)。インスタンスの各要素を比較し、比較結果を contains に代入することで等しいです。データは無駄ですが、クラス全体の構造が最適化され、パフォーマンスが向上しました。

インスタンスに含まれる各要素を比較するのではなく、ショートカット メソッドを使用して比較を実行します。側面:

ショートカット比較は、ハッシュ値を比較することによって、同じハッシュ コードを持つインスタンスは等価である必要はありませんが、等しいインスタンスは同じハッシュ値を持つ必要があります (またはそうである必要があります)。これらのデータ構造は、多くの場合、この手法を使用して名前が付けられ、それらを識別するために Hash code> によって名前を付けることができます。その中の <code>HashMap は、最も有名な代表的なものです

contains是比较浪费的,虽然整个类的数据结构进行了优化,能够提升性能。

他们通过使用一种快捷的方式(减少潜在的实例相等)进行比较,从而代替通过比较实例所包含的每个元素。而快捷比较仅需要比较下面这些方面:

快捷方式比较即通过比较哈希值,它可以将一个实例用一个整数值来代替。哈希码相同的实例不一定相等,但相等的实例一定具有有相同的哈希值。(或应该有,我们很快就会讨论这个)这些数据结构经常通过这种这种技术来命名,可以通过Hash来识别他们的,其中,HashMap是其中最著名的代表。

它们通常是这样这样运作的

  • 当添加一个元素,它的哈希码是用来计算内部数组索引(即所谓的桶)

  • 如果是,不相等的元素有相同的哈希码,他们最终在同一个桶上并且捆绑在一起,例如通过添加到列表。

  • 当一个实例来进行contains操作时,它的哈希码将用来计算桶值(索引值),只有当对应索引值上存在元素时,才会对实例进行比较。

因此equalshashCode是定义在<a href="http://www.php.cn/wiki/60.html" target="_blank">Object</a>类中。

散列法的思想

如果hashCode作为快捷方式来确定相等,那么只有一件事我们应该关心:相等的对象应该具有相同的哈希码,这也是为什么如果我们重写了equals方法后,我们必须创建一个与之匹配的hashCode实现的原因!

否则相等的对象是可能不会有相同的哈希码的,因为它们将调用的是Object's的默认实现。

HashCode 准则

引用自官方文档

hashCode通用约定:
* 调用运行Java应用程序中的同一对象,hashCode方法必须始终返回相同的整数。这个整数不需要在不同的Java应用程序中保持一致。
* 根据equals(Object)的方法来比较,如果两个对象是相等的,两个对象调用hashCode方法必须产生相同的结果。
* 根据equals(Object)的方法是比较,如果两个对象是不相等的,那么两个对象调用hashCode方法并不一定产生不同的整数的结果。但是,程序员应该意识到给不相等的对象产生不同的整数结果将有可能提高哈希表的性能。

第一点反映出了相等的一致性属性,第二个就是我们上面提出的要求。第三个阐述了一个重要的细节,我们将在稍后讨论。

HashCode实现

下面是非常简单的Person.hashCode的实现

@Override
public int hashCode() {
    return Objects.hash(firstName, lastName);
}

person’s是通过多个字段结合来计算哈希码的。都是通过Objecthash通常は次のように動作します

要素が追加されると、そのハッシュ コードを使用して内部 配列

。 http://www.php.cn/code/229.html" target="_blank">インデックス

(つまり、バケットと呼ばれます)

  • 「はい」の場合、等しくない要素は同じ ha ハッシュ コードを持ち、それらは同じバケットになり、リストに追加するなどしてまとめられます

  • インスタンスが contains オペレーションの対象となる場合、そのコードはバケット値 (インデックス値) の計算に使用され、インスタンスは要素が存在する場合にのみ比較されます。対応するインデックス値

  • 🎜 したがって、equals、<.code>hashCode<a href="http://www.%20php.cn/wiki/60.html" target="_blank">オブジェクト🎜</a> クラス。 🎜🎜ハッシュの考え方🎜🎜 hashCode が等しいかどうかを判断するためのショートカットとして使用される場合、注意すべき点は 1 つだけです。等しいオブジェクトは同じハッシュ コードを持つ必要があります。 equals メソッドをオーバーライドした後、一致する hashCode 実装を作成する必要があります。そうしないと、等しいオブジェクトはデフォルトを呼び出すため、同じハッシュ コードを持たない可能性があります。 オブジェクトの実装。 🎜🎜ハッシュコード ガイドライン🎜🎜公式ドキュメントからの引用🎜
    🎜 hashCode の一般規則: 🎜* Java アプリケーションを実行している同じオブジェクトに対して呼び出された場合、hashCode メソッドは常に同じ整数を返す必要があります。この整数は、異なる Java アプリケーション間で一貫している必要はありません。 🎜* 2 つのオブジェクトが等しい場合は、equals(Object) メソッドに従って比較します。2 つのオブジェクトに対して hashCode メソッドを呼び出すと、同じ結果が得られます。 🎜*quals(Object) メソッドによれば、2 つのオブジェクトが等しくない場合、2 つのオブジェクトに対して hashCode メソッドを呼び出しても、必ずしも異なる整数の結果が生成されるわけではありません。ただし、プログラマは、等しくないオブジェクトに対して異なる整数の結果を生成すると、ハッシュ テーブルのパフォーマンスが向上する可能性があることに注意する必要があります。 🎜
    🎜最初のポイントは同等の一貫性
    属性🎜を反映しており、2 番目のポイントはリクエストです上で作りました。 3 番目は、後で説明する重要な詳細を示しています。 🎜🎜ハッシュコードの実装🎜🎜 以下は、person.hashCode の非常に単純な実装です。🎜
    @Override
    public int hashCode() {
        return 0;
    }
    🎜person は、複数のフィールドを組み合わせてハッシュ コードを計算します。これらはすべて、Objecthash🎜関数🎜 を通じて計算されます。 🎜🎜フィールドを選択🎜🎜しかし、どのフィールドが関連しているのでしょうか? この要件は、次の質問の答えに役立ちます。等しいオブジェクトが同じハッシュ コードを持つ必要がある場合、計算されたハッシュ コードには、等価性チェック フィールドに使用されないフィールドが含まれるべきではありません。 (そうでない場合、2 つのオブジェクトはこれらのフィールドが異なるだけで、依然として等しい可能性があります。この場合、2 つのオブジェクトのハッシュ コードは異なります。)🎜🎜 したがって、ハッシュ グループ フィールドが等しい必要がある場合に使用されるフィールドのサブセットは、 。デフォルトでは、両方とも同じフィールドを使用しますが、考慮すべき詳細がいくつかあります。 🎜

    一致性

    首先,有一致性的要求。它应该相当严格。虽然它允许如果一些字段改变对应的哈希码发生变化(对于可变的类是不可避免的),但是哈希数据结构并不是为这种场景准备的。

    正如我们以上所见的哈希码用于确定元素的桶。但如果hash-relevant字段发生了改变,并不会重新计算哈希码、也不会更新内部数组。

    这意味着以后通过相等的对象,甚至同一实例进行查询也会失败,数据结构计算当前的哈希码与之前存储实例计算的哈希码并不一致,并是错误的桶。

    结论:最好不要使用可变字段计算哈希码!

    性能

    哈希码最终计算的频率与可能调用equals差不多,那么这里将是影响性能的关键部分,因此考虑此部分性能也是非常有意义的。并且与equals相比,优化之后又更大的上升空间。

    除非使用非常复杂的算法或者涉及非常多的字段,那么计算哈希码的运算成本是微不足道的、同样也是不可避免的。但是也应该考虑是否需要包含所有的字段来进行运算。集合需要特别警惕的对待。以Listssets为例,将会包含集合里面的每一个元素来计算哈希码。是否需要调用它们需要具体情况具体分析。

    如果性能是至关重要的,使用Objects.hash因为需要为varargs创建一个数组也许并不是最好的选择。但一般规则优化是适用的:不要过早地使用一个通用的散列码算法,也许需要放弃集合,只有优化分析显示潜在的改进。

    碰撞

    总是关注性能,这个实现怎么呢?

    @Override
    public int hashCode() {
        return 0;
    }

    快是肯定的。相等的对象将具有相同的哈希码。并且,没有可变的字段!

    但是,我们之前说过的桶呢?!这种方式下所有的实例将会有相同的桶!这将会导致一个链表来包含所有的元素,这样一来将会有非常差的性能。每次调用contains将会触发对整个list线性扫描。

    我们希望尽可能少的元素在同一个桶!一个算法返回变化多端的哈希码,即使对于非常相似的对象,是一个好的开始。

    怎样才能达到上面的效果部分取决于选取的字段,我们在计算中包含更多的细节,越有可能获取到不同的哈希码。注意:这个与我们所说的性能是完全相反的。因此,有趣的是,使用过多或者过少的字段都会导致糟糕的性能。

    防止碰撞的另一部分是使用实际计算散列的算法。

    计算Hsah

    最简单的方法来计算一个字段的哈希码是通过直接调用hashCode,结合的话会自动完成。常见的算法是首先在以任意数量的数值(通常是基本数据类型)反复进行相乘操作再与字段哈希码相加

    int prime = 31;
    int result = 1;
    result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
    result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
    return result;

    这可能导致溢出,但是不是特别有问题的,因为他们并没有产生Java异常。

    注意,即使是非常良好的的哈希算法也可能因为输入特定的模式的数据有导致频繁碰撞。作为一个简单的例子假设我们会计算点的散列通过增加他们的x和y坐标。当我们处理f(x) = -x线上的点时,线上的点都满足:x + y == 0,将会有大量的碰撞。

    但是:我们可以使用一个通用的算法,只到分析表明并不正确,才需要对哈希算法进行修改。

    总结

    我们了解到计算哈希码就是压缩相等的一个整数值:相等的对象必须有相同的哈希码,而出于对性能的考虑:最好是尽可能少的不相等的对象共享相同的哈希码。

    这就意味着如果重写了equals方法,那么就必须重写hashCode方法

    当实现hashCode

    • 使用与equals中使用的相同的字段(或者equals中使用字段的子集)

    • 最好不要包含可变的字段。

    • 对集合不要考虑调用hashCode

    • 如果没有特殊的输入特定的模式,尽量采用通用的哈希算法

    记住hashCode性能,所以除非分析表明必要性,否则不要浪费太多的精力。


    以上がJava で hashCode メソッドを実装するためのサンプル コードの共有の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。