我想用Java计算一组字符串的哈希值。是的,我可以对字符串进行排序并计算 使用
digest.update
进行 MD5 哈希迭代。
但我宁愿省略排序并使用类似 combineUnordered
https://github.com/google/guava/wiki/HashingExplained
有很多类似的问题提出相同的问题,例如Order-independent Hash Algorithm
但他们都没有提供一个简单的示例来展示如何在 Java 中迭代计算顺序无关的哈希值。
只需对每个哈希进行异或,顺序并不重要,而且哈希大小将是固定的,而不是随着集合的大小而增长。
使用内置java字符串哈希码的哈希码:
int hashcode = strings.stream()
.mapToInt(Object::hashCode)
.reduce(0, (left, right) -> left ^ right);
使用番石榴和MD5的哈希码就像所问的问题:
Optional<byte[]> hash = strings.stream()
.map(s -> Hashing.md5().hashString(s, Charset.defaultCharset()))
.map(HashCode::asBytes)
.reduce((left, right) -> xor(left, right));
static byte[] xor(byte[] left, byte[] right) {
if(left.length != right.length) {
throw new IllegalArgumentException();
}
byte[] result = new byte[left.length];
for(int i=0; i < result.length; i++) {
result[i] = (byte) (left[i] ^ right[i]);
}
return result;
}
您可以单独计算每个字符串的 MD5 哈希值,然后将它们全部相加以获得单个哈希值。这将是独立于顺序的。因为加法运算是可交换的。
这是一个示例(假设我们有一个方法 md5Hex(String str) 计算给定字符串的 md5 哈希值并以十六进制格式返回结果):
String[] strings = {"str1", "str2", "str3", ...};
BigInteger hashSum = BigInteger.ZERO;
for(String s : strings) {
String hexHash = md5Hex(s);
hashSum = hashSum.add(new BigInteger(hexHash, 16));
}
String finalHash = hashSum.toString(16);
这是一个使用 Guava 计算一组字符串的顺序无关哈希的示例:
import java.util.Set;
import com.google.common.base.Charsets;
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
...
public String hash(final Set<String> strings) {
final HashFunction function = Hashing.murmur3_128();
// Hashing.combineUnordered will throw an exception if input is empty.
if (strings.isEmpty()) {
return function.newHasher()
.hash()
.toString();
}
final List<HashCode> stringsHashes = strings.stream()
.map(string -> function.newHasher()
.putString(string, Charsets.UTF_8)
.hash())
.toList();
return Hashing.combineUnordered(stringsHashes).toString();
}