从 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);
}
Objects.hash(Object... values)
应该在您想要一个对象序列的散列的情况下使用,例如,当定义您自己的hashCode
方法并且想要一个简单编码的散列用于构成您的对象标识的多个值时。Objects.hashCode(Object o)
当您想要单个对象的哈希时应该使用,如果对象为空则不抛出。Object::hashCode()
当你想要单个对象的散列时应该使用,如果对象为空,将抛出异常。请注意,hash(o)
并且hashCode(o)
不一定会返回相同的东西!如果您正在为单个对象执行此操作,您可能应该使用hashCode
.
Objects.hashCode
实用程序方法Objects.hashCode( Object o )
只是调用hashCode
传递对象的方法。
那么为什么要发明或使用这种方法呢?为什么不自己调用对象的hashCode
方法呢?
这种方法有一个好处:NULL
➙ 0
。实用程序方法允许空值。
Objects.hashCode( myObject )
where myObject
is NULL
,你会得到一个零 (0)。myObject.hashCode()
when myObject
isNULL
会引发NullPointerException
参数。是否需要容忍 null 取决于您自己在特定情况下的判断。
Objects.hash
该实用程序方法Objects.hash( Object o , … )
用于不同的目的。该方法有两个阶段:
.hashCode
每个传递的对象,收集每个结果。如果您传递单个对象,Objects.hash( myObject )
, 首先myObject.hashCode
被调用并收集,然后计算该单个项目集合的哈希值。所以,你最终得到一个散列的散列。
在散列单个对象时,了解Objects.hashCode( myObject )
返回的结果与Objects.hash( myObject )
. 实际上,第二个返回第一个结果的散列。
这两种Objects
方法所采用的方法的逻辑本身是有意义的。
不幸的是,在实践中,对于我们这些试图在日常使用中使用它们的人来说,在我们的 POJO 上编写代码以覆盖hashCode
,相应地equals
,我们必须三思而后行来决定调用哪个。
hashCode
(and equals
) 覆盖基于您班级的单个成员,请使用Objects.hashCode( member )
.hashCode
(and 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
128 位值表示为一对 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 );
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() 方法。