序列化任意Java对象

时间:2016-08-31 09:16:25

标签: java serialization hash serializable

我目前需要序列化任意Java对象,因为我想使用Hash作为哈希表的键。在我读取默认hashCode经常发生冲突的各种警告之后,我想通过MessageDigest切换到散列来使用替代算法(例如SHA1,...),据说这些算法允许更多条目没有碰撞。 [作为旁注:我知道即使在这里碰撞也可能发生在早期,但我希望增加保持无碰撞的可能性。]

为实现这一目标,我尝试了this StackOverflow post中提出的方法。它使用以下代码获取byte[]所需的MessageDigest

public static byte[] convertToHashableByteArray(Object obj) {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutput out = null;
    byte[] byteOutput = null;

    try {
        out = new ObjectOutputStream(bos);
        out.writeObject(obj);
        byteOutput = bos.toByteArray();
    } catch (IOException io) {
        io.printStackTrace();
    } finally {
        try {
            if(out != null) { out.close(); }
        } catch(IOException io) {
            io.printStackTrace();
        }

        try {
            bos.close();
        } catch(IOException io) {
            io.printStackTrace();
        }
    }

    return byteOutput;
}

然而,这会导致只有实现serializable接口的对象被序列化/转换为byte[]的问题。为了解决此问题,我将toString()应用于obj子句中的给定catch,以强制在所有情况下获得byte[]

public static byte[] convertToHashableByteArray(Object obj) {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutput out = null;
    byte[] byteOutput = null;

    try {
        out = new ObjectOutputStream(bos);
        out.writeObject(obj);
        byteOutput = bos.toByteArray();
    } catch (IOException io) {
        String stringed = obj.toString();
        byteOutput = stringed.getBytes();
    } finally {
        try {
            if(out != null) { out.close(); }
        } catch(IOException io) {
            io.printStackTrace();
        }

        try {
            bos.close();
        } catch(IOException io) {
            io.printStackTrace();
        }
    }

    return byteOutput;
}

但是,对我来说,这仍然是完全错误的。所以我的问题是,是否有更好的替代方法将任意对象转换为byte[]以便能够计算哈希值。优选地,解决方案在不使用额外库的情况下工作,或者使用诸如Apache Commons之类的已建立的库。 (除此之外,我也开放其他方法来获取任意Java对象的SHA1 / SHA512哈希值。)

3 个答案:

答案 0 :(得分:0)

也许您可以将对象的UUID用作不可变的唯一标识符?

答案 1 :(得分:0)

这里有很多错误......

  1. 您应该使用equals和hashCode实现正确的密钥类,而不是使用随机对象。
  2. 序列化性能开销很容易意味着这样的映射将比平凡的迭代搜索慢。
  3. 在大多数情况下不应使用默认哈希码,因为从业务角度来看,对于“相等”的对象可能会有所不同。您应该使用equals重新实现hashcode(返回到第1点)。每当它由于指针别名而发生冲突时,如果它不能正常工作则无关紧要
  4. 关闭内存中流的方法过于复杂。只是一个接一个地关闭它们,它不是外部资源 - 如果失败,只是让它失败,你不需要在出现故障时100%关闭所有内容。您还可以使用一个可关闭的实用程序(或尝试/ catch资源)来避免一些开销
  5. 你不需要那个字节数组的复杂摘要 - 使用Arrays.hashCode,它对你的用例来说已经足够了(记住 - 不管怎么做,点1)
  6. 如果您仍在阅读并仍不愿意实施第1点,请返回第1点。再一次。

    最后回答你的问题,使用hessian序列化。

    http://hessian.caucho.com/doc/hessian-overview.xtp

    它非常类似于java,只是更快,更短的输出,并允许序列化不实现Serializable接口的对象(冒着搞乱的风险,你需要设置特殊标志来允许)。

答案 2 :(得分:-1)

如果你想序列化给定的对象,我建议你改变你的方法:

public static byte[] convertToHashableByteArray(Serializable obj){
     ..........
     ..........
}