与顺序无关的哈希算法

时间:2015-06-09 14:23:04

标签: java algorithm hash set

我目前正在为自定义编程语言开发一个集合库。我已经有了几种数据类型(Collection,List,Map,Set)和实现(可变和不可变),但到目前为止我所缺少的是hashCodeequals。虽然列表没有问题,因为它们是有序集合,但它们对集合和地图起着特殊的作用。如果两个集合具有相同的大小和相同的元素,则它们被认为是相等的,并且集合维护它们的顺序不应该在它们的相等性上有所不同。由于equals-hashCode-contract,hashCode实现也必须反映这种行为,这意味着具有相同元素但排序不同的两个集应具有相同的哈希码。 (这同样适用于地图,技术上是一组键值对)

示例(伪代码):

let set1: Set<String> = [ "a", "b", "c" ]
let set2: Set<String> = [ "b", "c", "a" ]
set1 == set2       // should return true
set1.hashCode == set2.hashCode // should also return true

我如何实现一个相当好的哈希算法,上面例子中的hashCode s返回相同的值?

3 个答案:

答案 0 :(得分:6)

JDK本身提出了以下解决此问题的方法。 java.util.Set接口的合同声明:

  

返回此set的哈希码值。集合的哈希码被定义为集合中元素的哈希码的总和,其中空元素的哈希码被定义为零。这确保了s1.equals(s2)意味着对于任何两个集合s1和s2的s1.hashCode()== s2.hashCode(),正如Object.hashCode()的常规协定所要求的那样。

使用条目总和的替代方法&#39;哈希码将使用例如^(XOR)运算符。

Scala语言使用Murmurhash算法的排序不变版本(参见私有scala.util.hashing.MurmurHash3类)来实现hashCode(或##)方法它的immutable sets和类似的集合。

答案 1 :(得分:1)

您可以按字母顺序计算对集合进行排序的哈希值。

有C#示例 - 我希望你能用Java翻译它:)

static String GetHash(List<String> l)
{
    using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
    {
        return BitConverter.ToString(md5.ComputeHash(l.OrderBy(p => p).SelectMany(s => System.Text.Encoding.ASCII.GetBytes(s + (char)0)).ToArray())).Replace("-", "");
    }
}

答案 2 :(得分:0)

这里是可能实现的伪代码:

String hashCode = null;
for(element : elements){
    hashCode = xor(hashCode, getHashCode(element));
}
return hashCode;

xor函数应该返回一个与两个参数中最长的字符串一样长的字符串。它将对每个中的位进行异或,直到它到达其中一个参数的末尾。然后它将从较长的字符串中取出剩余的位并将其附加到。

此实现意味着集合的hashCode与其最长元素的hashCode一样长。因为您正在对位进行异或,所以无论元素的顺序如何,哈希码都将是相同的。但是,与任何哈希实现一样,将有可能发生冲突。