在Java对象中存储大型十六进制数(MD5)的最有效方法是什么?

3
在考虑以下用例的情况下,在Java(或Groovy)对象中存储文件的MD5摘要的最有效方法(对性能和存储空间最优)是什么:
  1. 需要与数千个其他MD5摘要进行比较。
  2. 可能需要将其存储在HSQLDB中,以便根据MD5提取/分组记录
  3. 可以作为Map的键存储
我试图避免将其存储为字符串,因为字符串比较会更昂贵且占用更多空间。 如果使用BigInteger(string,radix),是否会更高效? 此外,在持久化到数据库时应选择哪种数据类型?
3个回答

5
创建一个类来封装一个byte[],并且不提供任何变异方法。如果您想在映射中将其用作键,则需要使其可比较或具有哈希码。使用byte[],您可以更轻松地从前32位计算简单的哈希码。

1
谢谢你的回答。使用Groovy的new BigInteger(1,digest).toString(16).padLeft(32,'0')可以处理从byte[]生成哈希值,但不确定是否“高效”。如果我必须在BigInt和String之间选择,你会建议我选择哪个?我问这个问题是因为这只是一个Groovy脚本,创建一个包装器可能过于复杂(如果它没有巨大的性能优势)。 - kdabir
1
@kunal,如果被迫在bigint和string之间选择,我会选择BigInteger。当你将一系列十六进制数字 naively 存储为字符串时,字符串的空间效率并不高。你可以将位打包到UTF-16代码单元中,但没有令人信服的理由去费这个劲。所有这些工作所能节省的最多只有16b,而已。 - Mike Samuel
再次感谢您的回答。正如@erickson所提到的,将其存储为长整型怎么样? - kdabir
@kunal,如果它适合16B,那当然可以。 - Mike Samuel

1

为了在Java中进行比较速度,将其存储为两个long值可能是最快的。对于持久性,如果您的数据库和持久性工具支持,则将其存储为字节数组是最合理的选择。否则,将其存储为十六进制或Base-64编码的文本是相当常见的,并且可以与访问同一数据库的其他应用程序很好地互操作。


0
如果您需要执行大量比较,可以将MD5值存储为2个long整数,这样您只需要执行最多4个逻辑操作即可检查另一个MD5值。
基本上,提供一个类,该类将接受输入、原始摘要数据作为byte[]并使用。
ByteBuffer bb = ByteBuffer.wrap(digestData);
long[] bits = new long[] {
    bb.getLong(),
    bb.getLong()
};

使用另一个long[] MD5数组进行比较

boolean eq = ((bits[0]^otherBits[0]) | (bits[1]^otherBits[1])) == 0);

使用以下方法重构MD5:

ByteBuffer bb = ByteBuffer.allocate(16);
bb.putLong(bits[0]);
bb.putLong(bits[1]);

byte[] digestData = new byte[16];
bb.get(digestData);

注意:我并不建议将byte[]转换为long[]进行每次比较,这只是为了存储摘要以进行比较。最后的重构片段是可选的,您应该将数据保留为byte[],仅比较long[]数组。在数据库中,将数据存储为32字节的十六进制值。


-1:long类型始终为64位,无论JVM版本如何。而且不需要进行所有这些转换。Long.SIZE是一个常数。检查它没有意义。只需将其存储为字节数组,并使用java.util.Arrays.equals(byte [],byte [])即可。 - JB Nizet
@JB Nizel,这个问题是关于性能的,而检查Arrays.equals(byte[], byte[])的效率比执行我建议的检查要低。该API存在是为了方便,虽然在大多数情况下我通常建议使用Java API,但这里不是这种情况。 - Yanick Rochon
在 MessageDigest 中,public static boolean isEqual(byte[] digesta, byte[] digestb) 可以比较两个摘要(byte[])。但是,我不确定性能如何。 - kdabir
@kunal,它仍然比比较两个“long”值要慢。方法“isEqual”不考虑消化的方法,因此它只执行与“Arrays.equals”类似的操作。 - Yanick Rochon

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