【问题标题】:Java hashCode() differs in different executions of same object creationJava hashCode() 在同一对象创建的不同执行中有所不同
【发布时间】:2017-01-31 10:40:05
【问题描述】:

基本信息: 我有一个生成 MyCustomObject 的 MyCustomObjectGenerator 。此对象(应该)始终使用相同的值创建。这个对象包含 mutch 代码、接口、枚举、子类......,所以我在这里保持简单。所有对象或接口的实现都覆盖了 equals & hashCode 方法(希望以正确的方式)。

此 MyCustomObject 使用 Jackson 和自定义序列化程序序列化为 JSON(MyCustomObject 不包含任何 Jackson 依赖项,如 Jackson 注释!)。

每个 JSON 都会根据 MyCustomObject 的 hashCode 计算得到一个 ID(参见下面的代码)。此 id 仅用作校验和,以非常快速地识别相同的 json。还有另一个基于 UUID 的 ID,用于标识作业本身,因此我知道 2 个作业可以具有相同的校验和!

问题: 有两个 JUnit 测试(一个 Junit 测试类中的最小和最大方法),它们生成一个 JSON 并使用来自 File 的预定义 JSON 来检查这个 JSON。如果我同时运行两个测试/方法,则 JSON 与文件中的那个匹配,但如果我只运行 testMaximal() 方法,则断言失败,因为生成的 id 不一样。所以 hashCode 似乎有所不同。如果我再次启动这两个测试方法,jsons 将再次与来自 file 的那个匹配,因此生成的对象不包含任何随机内容,如 ZonedDateTime.now()。其他 JSON 值始终相同,只是 ID 不同。如果执行(2 个方法/1 个方法)条件相同,则 HashCode 似乎相同,但如果更改此执行条件,则 HashCode 会有所不同。这对我来说真的很奇怪。

现在我必须评估,哪个类没有正确覆盖(或产生不同的 hashCode)hashCode 方法(id 基于所有包含对象的 hashCodes)。有人有什么好主意通过反射打印 MyCustomObject 的每个对象、变量、子类、接口...的 hashCode 吗?我已经试过了

ReflectionToStringBuilder.toString(myCustomObject, ToStringStyle.DEFAULT_STYLE)

但这不会打印 myCustomObject 的每个子子元素的 hashCode。

如果我可以打印准确的对象值,包括。 hashValue,然后我可以比较它。

我已经找到了一个与 ReflectionToStringBuilder.toString() 不同的对象,但是这个对象本身包含 mutch 接口、变量等,但是这个 BlablaObject@4fb64261[...] 中的所有值都是相同的,并且hashCode 丢失

最重要的问题: 是否存在已知情况,hashCode() 的行为很奇怪,例如“如果您在 HashMap 中使用枚举作为键,那么 hashCode 取决于 java 堆栈或 JVM 版本”或某事。像这样。


代码

MyCustomObjectGenerator .java

    public class MyCustomObjectGenerator {

    private MyCustomObject(){};

    public static MyCustomObject generate(boolean isMinimal){
        //if minimal then create minimal object
        //if minimal == false then create maximized object**strong text**
        MyCustomObject myCustomObject = new MyCustomObject(...);
        myCustomObject.setXY(...)
        ...
        return myCustomObject;
    }
}

MyCustomObject.java

import javax.xml.bind.DatatypeConverter;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;

public class MyCustomObject {
    //variables, enums, interface ... here
    ...    
    public MyCustomObject(...){...}

    //mutch code here
    ...

    public String getChecksum() {
        String id = Integer.toString(hashCode());
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(id.getBytes("UTF-8"));
            id = DatatypeConverter.printHexBinary(digest);
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
            // do nothing here
        }
        return new id;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(-1013166723, 372138085)
        //if needed in extended classes: .appendSuper(super.hashCode())
        .append(...)
        ....
        .toHashCode();
    }

    @Override
    public boolean equals(
            final Object other) {
        if (!(other instanceof MyCustomObject)) {
            return false;
        }
        MyCustomObject castOther = (MyCustomObject) other;
        return new EqualsBuilder()
            // if needed in extended classes: .appendSuper(super.hashCode())
            .append(..., ...)
            ....
            .isEquals();
    }
}

【问题讨论】:

  • Enum 的默认 hashCode() 被定义为 super.hashCode() (java.lang.Object 的实现),所以它对于每个 JVM 实例都不同,仅此而已。 HashCode 合约规定:“这个整数不需要从一个应用程序的一次执行到同一应用程序的另一次执行保持一致。”所以我猜你有它:要么覆盖每个 hashCode() 方法,即使是枚举,以确保它们在 JVM 实例中的稳定性,或者你的哈希值会因每个实例而异。

标签: java json hashcode md5sum


【解决方案1】:

有很多类型,其中hashCode() 返回的值会随着应用程序运行的不同而不同。这包括:

  • 所有数组类型
  • 所有enum 类型
  • 许多其他类型的对象相等性和对象身份是相同的;例如ObjectClassThreadStringBuilderStringBuffer

作为一般规则,如果 Java SE 定义的类没有记录为具有与 Object.equals(Object) 语义不同的 equals 方法,那么您应该假设它也使用Object.hashCode() ... 并且哈希码会因运行而异。

【讨论】:

    【解决方案2】:

    您不应该使用hashCode(),除非在基于散列的数据结构中选择存储桶。正如您所发现的,它当然没有在验证中的位置。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-20
      • 2013-04-16
      相关资源
      最近更新 更多