自Java 7以来,我们拥有了
o.hashCode();
Objects.hashCode(o);
Objects.hash(o);
前两个与空值检查大致相同,但最后一个是什么?
当提供单个对象引用时,返回的值不等于该对象引用的哈希码。
为什么会这样?我的意思是,我们不需要三个做同样事情的方法,我明白这一点,但为什么我们需要Objects.hash()
呢?在何时选择使用其中之一?
请参考hashCode
和 hash
文档。其中,hash
接受参数为 Object...
类型,而 hashCode
接受参数为 Object
类型。以下是示例:
@Override public int hashCode() {
return Objects.hash(x, y, z);
}
hashCode
方法,并且想要用于组成对象标识的多个值的简单哈希码时,应使用Objects.hash(Object... values)
。Objects.hashCode(Object o)
。Object::hashCode()
。请注意,hash(o)
和hashCode(o)
返回的值可能不同!如果只针对单个对象进行操作,则应该使用hashCode
。
Objects.hashCode
Objects.hashCode( Object o )
只是在传递的对象上调用hashCode
方法。hashCode
方法呢?Objects.hashCode( myObject )
,其中myObject
为NULL
,则会返回零(0)。myObject
为NULL
时调用myObject.hashCode()
会抛出NullPointerException
异常。Objects.hash
Objects.hash( Object o , … )
有不同的用途。该方法有两个阶段:
.hashCode
,并收集每个结果。如果您传递单个对象Objects.hash( myObject )
,首先会调用myObject.hashCode
并收集结果,然后计算单项收集的哈希值。因此,您最终得到一个哈希的哈希。
当对单个对象进行哈希处理时,了解Objects.hashCode( myObject )
返回的结果与Objects.hash( myObject )
返回的结果不同至关重要。实际上,第二个返回的是对第一个结果的哈希。
这两种Objects
方法采取的方法逻辑本身是有意义的。
不幸的是,在实践中,对于我们在日常使用中尝试在POJO上编写代码以覆盖hashCode
和相应的equals
的人来说,我们必须三思而后行,以决定调用哪个方法。
hashCode
(和equals
)重写是基于类的单个成员,请使用Objects.hashCode(member)
。hashCode
(和equals
)重写是基于类的多个属性,请使用Objects.hash(memberA, memberB, memberC)
。@Override
public int hashCode() {
return this.member.hashCode() ; // Throws NullPointerException if member variable is null.
}
@Override
public int hashCode() {
return Objects.hashCode( this.member ) ; // Returns zero (0) if `this.member` is NULL, rather than throwing exception.
}
@Override
public int hashCode() {
return Objects.hash( this.memberA , this.memberB , this.memberC ) ; // Hashes the result of all the passed objects’ individual hash codes.
}
我们可以非常简单地测试这些不同的方法。
让我们以UUID
对象为例。UUID(通用唯一识别码)是一个128位的值,其中某些位具有特定的语义。
OpenJDK实现将UUID
内部表示为一对64位long
整数数字。
同样的实现覆盖了Object::equals
和Object::hashCode
,以查看存储在这一对长整型中的数据。以下是这两种方法的源代码。
public boolean equals(Object obj) {
if ((null == obj) || (obj.getClass() != UUID.class))
return false;
UUID id = (UUID)obj;
return (mostSigBits == id.mostSigBits &&
leastSigBits == id.leastSigBits);
}
public int hashCode() {
long hilo = mostSigBits ^ leastSigBits;
return ((int)(hilo >> 32)) ^ (int) hilo;
}
实例化我们的 UUID 对象。
UUID uuid = UUID.randomUUID();
计算我们的哈希值。
int hash1 = uuid.hashCode();
int hash2 = Objects.hashCode( uuid ); // Result matches line above.
int hash3 = Objects.hash( uuid ); // Returns a hash of a hash.
int hash4 = Objects.hash( uuid.hashCode() ); // Result matches line above.
输出到控制台。
System.out.println( "uuid.toString(): " + uuid.toString() );
System.out.println( " 1/2 = " + hash1 + " | " + hash2 );
System.out.println( " 3/4 = " + hash3 + " | " + hash4 );
在IdeOne.com上查看此代码的实时运行。
uuid.toString(): 401d88ff-c75d-4607-bb89-1f7a2c6963e1
1/2 = 278966883 | 278966883
3/4 = 278966914 | 278966914
默认情况下,Object类的hashCode()方法返回对象的内存地址。因此,如果您有以下类:
class Car {
String make;
String model;
int year;
public Car(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
}
然后创建两个对象:
Car car1 = new Car("Toyota", "Corolla", 2010);
Car car2 = new Car("Toyota", "Corolla", 2010);
car1.hashCode()将与car2.hashCode()不同,因为每个对象都有不同的内存地址。
如果您希望car1和car2返回相同的哈希码,那么您应该覆盖Car类的默认Object hashCode()方法,如下所示:
@Override
public int hashCode() {
Object[] x = {model, make, Integer.valueOf(year)};
int hashArray = Arrays.hashCode(x);
return hashArray;
}
这将使得car1.hashCode()等于car2.hashCode(),因为String.hashCode()是基于字符串内容计算hashCode的,而Integer.hashCode()将返回整数值本身。
在Java 7中,你可以使用Objects.hash(Object... values)。因此我们的新Car hashCode()将如下所示:
@Override
public int hashCode() {
return Objects.hash(model, make, year);
}
Objects.hash(Object... values)会为您调用Arrays.hashCode。
最后,Objects.hashCode(Object o)将执行空值检查。如果该对象为空,则返回0。否则,它将调用对象的hashCode()方法。
hashCode()
已经有大约20年没有返回内存地址了。它实际上无法这样做。GC会移动对象。所谓的内存地址是一个不可移动的句柄。 - user207421https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/Objects.java
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
Arrays.hashCode
对不同类型有不同的实现。例如,Object 的实现如下:public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
hash(o)
和hashCode(o)
不返回相同值的情况是什么?在hash()
文档中指出:警告: 如果提供单个对象引用,则返回的值与该对象引用的哈希码不相等。
,但我仍在努力弄清原因。 - Craig OtisObjects.hash
只是调用了Arrays.hashCode
方法 (http://hg.openjdk.java.net/jdk10/jdk10/jdk/file/777356696811/src/java.base/share/classes/java/util/Objects.java#l145),而一个单独对象的hashCode
值与仅包含该单个对象的数组的hashCode
值是不同的 (http://hg.openjdk.java.net/jdk10/jdk10/jdk/file/777356696811/src/java.base/share/classes/java/util/Arrays.java#l4677)。 - Peter Hull