基本信息:我有一个生成MyCustomObject的MyCustomObjectGenerator。此对象(应该是)始终使用相同的值创建。此对象包含多个代码、接口、枚举、子类。。。,所以我在这里保持简单。接口的所有对象或实现都覆盖equals&hashCode方法(希望以正确的方式)。
此MyCustomObject使用自定义序列化程序通过Jackson序列化为JSON(MyCustomObject不包含任何类似Jackson注释的Jackson依赖项!)。
每个JSON都会获得一个基于MyCustomObject的hashCode计算的ID(请参阅下面的代码)。这个id只是作为校验和来快速识别相同的json。还有另一个基于UUID的ID,它标识作业本身,所以我知道两个作业可以有相同的校验和!
问题:有两个JUnit测试(一个JUnit测试类中的最小和最大方法),它们生成一个JSON,并使用File中预定义的JSON检查该JSON。如果我运行两个测试/方法,JSON将与文件中的一个匹配,但如果我只运行testMaximal()方法,则断言将失败,因为生成的id不相同。因此,hashCode似乎有所不同。如果我再次启动这两个测试方法,jsons将再次与文件中的方法匹配,因此生成的对象不包含任何随机内容,如ZonedDateTime.now()。其他JSON值始终相同,只是ID不同。如果执行(2个方法/1个方法)条件相同,则HashCode看起来是相同的,但如果更改了此执行条件,则会有所不同。这对我来说真的很奇怪。
现在我必须评估,哪个类没有正确地覆盖(或产生不同的hashCode)hashCode方法(id基于所有包含对象的hashCodes)。有人有什么好主意打印每个对象、变量、子类、接口的hashCode吗。。。通过反射的MyCustomObject?我已经试过了
ReflectionToStringBuilder.toString(myCustomObject, ToStringStyle.DEFAULT_STYLE)
但这不会打印myCustomObject的每个子元素的hashCode。
如果我可以打印包括hashValue在内的精确对象值,那么我可以进行比较。
我已经找到了一个与ReflectionToStringBuilder.toString()不同的对象,但这个对象本身包含多个接口、变量等,但其中的所有值BlablaObject@4fb64261[…]是相同的,并且那里的hashCode缺少
最重要的问题是:是否有已知的情况,hashCode()的行为很奇怪,比如"如果你在HashMap中使用enum作为键,那么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();
}
}
有很多类型的应用程序运行不同,hashCode()
返回的值会有所不同。这包括:
- 所有数组类型
- 所有
enum
类型 - 许多其他类型的对象相等性和对象同一性是相同的;例如
Object
、Class
、Thread
、StringBuilder
和StringBuffer
一般来说,如果Java SE定义为的类没有被记录为具有与Object.equals(Object)
在语义上不同的equals
方法,那么您应该假设它也使用Object.hashCode()
。。。并且哈希代码将随着每次运行而变化。
除了在基于哈希的数据结构中选择bucket之外,不应该使用hashCode()
。正如你所发现的,它在验证中肯定没有立足之地。