如何解决番石榴库:Objects.hashCodeObject []碰撞安全吗?
| 在研究覆盖ѭ0的不同选项时,我被定向到Google的番石榴库(javadoc)中的Objects.hashCode(Object[])
。 Javadoc声明将其委托给Arrays.hashCode(Object[])
。在许多不同的对象类型中使用此方法是否安全?这不是容易发生哈希冲突,还是不可能仅因为容器通常仅包含一种类型的对象而导致?
作为一个简单的示例,请考虑以下类,
public class Student {
private final String name;
public Student(String name) {
this.name = name;
}
@Override
public int hashCode() {
return Objects.hashCode(name);
}
}
public class Teacher {
private final String name;
public Teacher(String name) {
this.name = name;
}
@Override
public int hashCode() {
return Objects.hashCode(name);
}
}
public class HashCodeDriver {
public static void main(String[] args) {
final String name = \"moe\";
Student s = new Student(name);
Teacher t = new Teacher(name);
long studentHash = s.hashCode();
long teacherHash = t.hashCode();
System.out.println(\"studentHash=\" + studentHash + \" teacherHash=\" + teacherHash);
if(studentHash == teacherHash) {
System.out.println(\"hash codes match\");
}
else {
System.out.println(\"hash codes don\'t match\");
}
}
}
输出:
studentHash=108322 teacherHash=108322
hash codes match
对象是两种不同的类型,但是生成相同的哈希码。这不是问题吗?我是否应该将类作为第一个参数传递来防止发生这种冲突?例如,
public class Student {
private final String name;
public Student(String name) {
this.name = name;
}
@Override
public int hashCode() {
return Objects.hashCode(Student.class,name);
}
}
public class Teacher {
private final String name;
public Teacher(String name) {
this.name = name;
}
@Override
public int hashCode() {
return Objects.hashCode(Teacher.class,name);
}
}
这就是Javadoc警告仅向此方法提供单个对象的原因吗?在javadoc中,
警告:提供单个对象时,返回的哈希码不等于该对象的哈希码。
解决方法
当2种不同类型的2个不同对象具有相同的哈希码时,这不是问题。
希望,当您要构建
HashMap
时,您不会将“学生和教师”混为一谈。而且即使您想做HashMap<Object,Object>
也可以,因为
assertFalse( new Teacher( \"John Smith\" ).equals( new Student( \"John Smith\" ) );
这就是为什么必须同时覆盖ѭ9和ѭ10的原因。
委托.2ѭ的唯一缺点可能是有时从性能角度来看可能太昂贵了。
例如,在您的情况下,对于教师或学生而言,这将是一种更好的哈希方法。
@Override
public int hashCode() {
return name.hashCode();
}
, 警告仅表示x.hashCode() != Objects.hashCode(x)
是正确的。 (好吧,大多数情况下都是这样。它们仍然可能会碰撞某些值。实际上,对于大多数对象而言,这是不相等的。)
有效的hashCode / equals实现为:
public class Teacher {
private final String name;
public Teacher(String name) {
this.name = name;
}
@Override public equals(Object obj){
if(obj == this) return true;
if(!(obj instanceof Teacher)) return false;
return Objects.equal(name,((Teacher) obj).getName());
}
@Override public hashCode(){
return 0;
}
}
尽管所有哈希值都发生冲突,但这是有效的。从hashCode()javadoc:
不需要两个对象
根据
equals(java.lang.Object)方法,然后
分别对每个调用hashCode方法
这两个对象必须产生不同的
整数结果。
与“普通”实现的区别在于此代码的性能将大大降低。例如,HashMaps将退化为类似查找性能的列表。
即使使用此实现:
@Override
public int hashCode() {
return Objects.hashCode(Teacher.class,name);
}
不同类的哈希值可能会(但不太可能)冲突。如果两个类的类名称的哈希值相同,就是这种情况。
当在内部使用hashCode()的集合中有许多来自不同类型,名称相同的实例时,这种优化应该是最后的选择。总体效果是有限的:如果您有n种类型,那么由于这种情况,您最多只能发生n次碰撞。其他因素可能会主导性能特征。
, 如果您在同一地图的键集中混合了许多不同的混凝土类型,则仍可以使用Objects.hashCode()
并通过将输出与每种混凝土类型使用不同的值进行异或来最大程度地减少冲突。
class Class1 {
public int hashCode() {
return Object.hashCode(...) ^ 0x12b7eff8;
}
}
class Class2 {
public int hashCode() {
return Object.hashCode(...) ^ 0xe800792b;
}
}
通过对随机选择但稳定的每个具体类的值进行异或运算,可以消除仅由于Object.hashCode
的参数相等而可能发生的冲突的机会。
警告:提供单个对象时,返回的哈希码不等于该对象的哈希码。
这就是Javadoc警告仅向此方法提供单个对象的原因吗?在javadoc中,
否。此警告不是关于具有相同成员的不同具体类的实例之间发生碰撞的机会。相反,由于假设单个值的散列与“ 19”的散列相同,因此在散列码匹配中警告有关否定否定。
例如,请看下面在不正确的快速跟踪代码中所作的假设,该代码试图通过使用缓存的哈希码来避免相等检查:
class Name {
int cachedHashCode;
...
}
class Person {
int cachedHashCode; // 0 if not computed
private final Name name;
public boolean hasName(Name n) {
return ((cachedHashCode != 0 && n.cachedHashCode != 0)
&& cachedHashCode == n.cachedHashCode)
|| n.equals(name);
}
public int hashCode() {
if (cachedHashCode == 0) { cachedHashCode = Object.hashCode(name); }
return cachedHashCode;
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。