객체 의 고유한 정체성은 단지 좋은 등호를 작성하는 것만으로는 될 수 없다는 것을 알고 계실 것입니다.
을 구현하는 것도 좋지만 이제 hash코드 방법도 구현해야 합니다. 🎜>
Equality와 Hash를 올바르게 수행하는 방법을 살펴보겠습니다. 코드평등은 일반적인 관점에서 볼 때 해시 코드는 더 기술적이며 이해하기 어려운 경우 성능 향상을 위한 구현 세부 사항일 뿐이라고 할 수 있습니다. 대부분의 데이터입니다. 구조체는 요소가 포함되어 있는지 확인하기 위해 equals 메소드를 사용합니다. 예:List<String> list = Arrays.asList("a", "b", "c"); boolean contains = list.contains("b");이
변수
에는 "b"가 동일한 인스턴스가 아니지만 결과가 포함됩니다. 문자열 상주),의 각 요소를 비교한 다음 비교 결과를 에 할당하면 동일합니다. 비록 전체 클래스 데이터 구조가 성능 향상을 위해 최적화되었지만
포함된 인스턴스를 비교하는 대신 단축 방법(잠재적인 인스턴스 동일성을 줄임)을 사용하여 비교를 수행합니다. 단축 비교는 다음 측면만 비교하면 됩니다. contains
로 식별할 수 있는데, 그중
가 가장 유명한데요.Hash
보통은 HashMap
의 인덱스
(소위 버킷)를 계산하는 데 사용됩니다. 즉, 동일하지 않은 요소는 동일한 해시 코드를 가지며 동일한 버킷에 저장되고 함께 묶입니다. 예를 들어 목록에 추가하면contains
해싱의 개념
메서드를 재정의하는 경우 일치하는 equals
구현을 만들어야 하는 이유입니다! 🎜>hashCode
그렇지 않으면 동일한 객체가 <a href="http://www.php.cn/wiki/60.html" target="_blank">Object</a>
의 기본 구현을 호출하기 때문에 동일한 해시 코드를 갖지 않을 수 있습니다.
hashCode
equals
인용hashCode
공식 문서
Object's
hashCode 일반 규칙:
HashCode
* equals(Object) 메소드에 따르면 두 객체가 동일하지 않은 경우 두 객체에 대해 hashCode 메소드를 호출해도 반드시 다른 정수 결과가 생성되는 것은 아닙니다. 그러나 는 동일하지 않은 객체에 대해 서로 다른 정수 결과를 생성하면 잠재적으로 해시 테이블 성능이 향상된다는 점을 인식해야 합니다.
첫 번째 점은 동등 일관성속성person의 여러 필드를 결합하여 해시 코드를 계산하는 매우 간단한 구현입니다. 모두 를 통해 계산됩니다. 필드 선택을 반영하고, 두 번째 점은 위에서 만든 요구 사항입니다. 세 번째는 나중에 논의할 중요한 세부 사항을 보여줍니다.
HashCode 구현
다음은@Override public int hashCode() { return Objects.hash(firstName, lastName); }
Person.hashCode
그러나 어떤 필드가 관련되어 있습니까? 요구 사항은 다음 질문에 답하는 데 도움이 됩니다. 동일한 개체가 동일한 해시 코드를 가져야 하는 경우 해시 코드 계산은 사용되지 않습니다. 동등성 검사가 포함되어야 합니다. (그렇지 않으면 두 개체가 해당 필드에서만 다르지만 여전히 동일할 수 있습니다. 이 경우 두 개체의 해시 코드가 달라집니다.)
그래서 해시 그룹 필드를 사용해야 할 때 사용됩니다. 필드의 하위 집합입니다. 기본적으로 둘 다 동일한 필드를 사용하지만 고려해야 할 몇 가지 세부 사항이 있습니다. Object
首先,有一致性的要求。它应该相当严格。虽然它允许如果一些字段改变对应的哈希码发生变化(对于可变的类是不可避免的),但是哈希数据结构并不是为这种场景准备的。
正如我们以上所见的哈希码用于确定元素的桶。但如果hash-relevant字段发生了改变,并不会重新计算哈希码、也不会更新内部数组。
这意味着以后通过相等的对象,甚至同一实例进行查询也会失败,数据结构计算当前的哈希码与之前存储实例计算的哈希码并不一致,并是错误的桶。
结论:最好不要使用可变字段计算哈希码!
哈希码最终计算的频率与可能调用equals
差不多,那么这里将是影响性能的关键部分,因此考虑此部分性能也是非常有意义的。并且与equals
相比,优化之后又更大的上升空间。
除非使用非常复杂的算法或者涉及非常多的字段,那么计算哈希码的运算成本是微不足道的、同样也是不可避免的。但是也应该考虑是否需要包含所有的字段来进行运算。集合需要特别警惕的对待。以Lists
和sets
为例,将会包含集合里面的每一个元素来计算哈希码。是否需要调用它们需要具体情况具体分析。
如果性能是至关重要的,使用Objects.hash
因为需要为varargs
创建一个数组也许并不是最好的选择。但一般规则优化是适用的:不要过早地使用一个通用的散列码算法,也许需要放弃集合,只有优化分析显示潜在的改进。
总是关注性能,这个实现怎么呢?
@Override public int hashCode() { return 0; }
快是肯定的。相等的对象将具有相同的哈希码。并且,没有可变的字段!
但是,我们之前说过的桶呢?!这种方式下所有的实例将会有相同的桶!这将会导致一个链表来包含所有的元素,这样一来将会有非常差的性能。每次调用contains
将会触发对整个list线性扫描。
我们希望尽可能少的元素在同一个桶!一个算法返回变化多端的哈希码,即使对于非常相似的对象,是一个好的开始。
怎样才能达到上面的效果部分取决于选取的字段,我们在计算中包含更多的细节,越有可能获取到不同的哈希码。注意:这个与我们所说的性能是完全相反的。因此,有趣的是,使用过多或者过少的字段都会导致糟糕的性能。
防止碰撞的另一部分是使用实际计算散列的算法。
最简单的方法来计算一个字段的哈希码是通过直接调用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 중국어 웹사이트의 기타 관련 기사를 참조하세요!