如何为Java对象生成校验和

22

我正在寻找一种解决方案,用于为任何类型的Java对象生成校验和,并且该校验和对于生成相同对象的应用程序的每次执行都保持不变。

我尝试使用Object.hashCode(),但API指出:

....这个整数在应用程序的一个执行到相同应用程序的另一个执行之间不需要保持一致。


你能添加一个引用链接吗? - Roman
@Roman:http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#hashCode%28%29 - Tadeusz Kopec for Ukraine
7
int checksum(Object o) { return 1;} 的翻译如下:int checksum(Object o){ return 1; } - Tadeusz Kopec for Ukraine
更详细地说,我必须检查两个应用程序调用之间的对象(编组的 XML 请求)是否相同。 - Alex
1
@Alex:那你为什么不比较原始的 XML 是否相等呢?这可能比你现在尝试做的任务要容易得多(即使你让它容忍空格或类似的细微差异)。 - Joachim Sauer
我同意Joachim的观点,因为使用哈希值(校验和)不仅简单,而且由于碰撞而导致的错误很少,但并非完全没有错误。 - Marcus Adams
11个回答

17
public static String getChecksum(Serializable object) throws IOException, NoSuchAlgorithmException {
    ByteArrayOutputStream baos = null;
    ObjectOutputStream oos = null;
    try {
        baos = new ByteArrayOutputStream();
        oos = new ObjectOutputStream(baos);
        oos.writeObject(object);
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] thedigest = md.digest(baos.toByteArray());
        return DatatypeConverter.printHexBinary(thedigest);
    } finally {
        oos.close();
        baos.close();
    }
}

17

我曾经遇到过类似的问题(为XML文件生成好的哈希值),后来发现最好的解决方案是使用MessageDigest中的MD5,或者如果需要更快的速度,则可以使用Fast MD5。请注意,即使Object.hashCode每次都相同,它也太短了(仅32位)无法确保高度唯一性。我认为64位是计算良好哈希码的最低要求。请注意,MD5生成的哈希码长度为128位,这甚至比在此情况下所需的长度还要长。

当然,要使用MessageDigest,您需要首先序列化(在您的情况下进行编组)对象。


感谢大家的回答。我从编组请求中计算出一个MD5。 - Alex

8

例子

private BigInteger checksum(Object obj) throws IOException, NoSuchAlgorithmException {
// 如果对象为空,则返回0 if (obj == null) { return BigInteger.ZERO; }
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); oos.close();
// 创建SHA1实例并更新数据 MessageDigest m = MessageDigest.getInstance("SHA1"); m.update(baos.toByteArray());
// 返回摘要结果 return new BigInteger(1, m.digest()); }

5

我认为你应该看一下序列化。序列化机制需要解决类似的问题,因此你可以看看它是如何实现的。

但如果你描述一下你想要解决的问题,你可能会得到更精确的解决方案。


4
如果您控制源代码,可以实现hashCode()函数,使其在不同的执行环境下保持一致。

3

您是否希望能够对所有Java对象执行此操作?

在这种情况下,hashCode() 不起作用。

对于某些类,hashCode() 具有更严格的定义,可以保证在多次执行中相等。例如,String 具有明确定义的 hashCode 实现。类似地,ListSet 也具有明确定义的值,前提是它们包含的所有对象也具有明确定义的值(请注意,一般的Collection.hashCode() 不需要该值是明确定义的)。

对于其他类,您将必须使用反射递归地应用某些明确定义的公式来构建校验和。


2

Apache Commons Lang 库提供了一个 HashCodeBuilder 类,它可以帮助构建哈希码,以满足您对类属性的要求。

示例:

   public int checksum() {
     // you pick a hard-coded, randomly chosen, non-zero, odd number
     // ideally different for each class
     return new HashCodeBuilder(17, 37).
       append(property1).
       append(property2).
       append(property3).
       toHashCode();
   }

请参见Commons Lang API

2

哈希码没问题。如果给定的类覆盖了equals方法,并且按照合同要求覆盖了hashcode方法,那么就没问题了。按照合同,如果equals返回true,则hashcode必须相同。
或者该类没有覆盖equals方法。在这种情况下,应用程序的不同执行不能产生相同的对象,因此不存在问题。
唯一的问题是,某些类(甚至来自Java API)违反了equals的合同。


2
如果您正在使用Eclipse IDE,那么它有一个操作(在Source菜单下)可以生成hashcode和equals函数。它允许您选择类中要包含在hashcode中的属性。这类似于使用已经建议过的HashCodeBuilder方法。
或者,您可以将对象流式传输到字节数组中并生成其MD5。

0
/*
 * Calculate checksum of a File using MD5 algorithm
 */
public static String checkSumApacheCommons(Object obj){
    String checksum = DigestUtils.md5Hex(String.valueOf(obj));
    return checksum;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接