73

从 Java 7 开始,我们有

o.hashCode();
Objects.hashCode(o);
Objects.hash(o);

前两个与空检查大致相同,但最后一个是什么?

当提供单个对象引用时,返回值不等于该对象引用的哈希码。

这是为什么?我的意思是,我们不需要三种方法来做同样的事情,我理解,但为什么我们需要Objects.hash()呢?你什么时候会选择使用一个而不是另一个?

4

3 回答 3

93

请参阅 和 的hashCode文档hashhash需要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.

于 2012-07-25T18:55:37.987 回答
41

Objects.hashCode

实用程序方法Objects.hashCode( Object o )只是调用hashCode传递对象的方法。

容忍 NULL

那么为什么要发明或使用这种方法呢?为什么不自己调用对象的hashCode方法呢?

这种方法有一个好处:NULL0。实用程序方法允许空值。

  • 如果你调用Objects.hashCode( myObject )where myObjectis NULL,你会得到一个零 (0)。
  • 相反,调用myObject.hashCode()when myObjectisNULL会引发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 ).

单个成员,不容忍 NULL

@Override
public int hashCode() {
    return this.member.hashCode() ;  // Throws NullPointerException if member variable is null.
}

单个成员,容忍 NULL

@Override
public int hashCode() {
    return Objects.hashCode( this.member ) ;  // Returns zero (0) if `this.member` is NULL, rather than throwing exception.
}

多成员,容忍 NULL

@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对象为例。UUID通用唯一标识符)是一个128 位的值,其中某些位具有某些语义。

OpenJDK内部实现将UUID128 位值表示为一对 64 位long整数。

相同的实现覆盖Object::equalsObject::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

于 2018-10-04T19:42:42.697 回答
15

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() 方法。

于 2013-12-08T16:56:50.127 回答