我目前正在为自定义编程语言开发一个集合库。我已经有了几种数据类型(Collection,List,Map,Set)和实现(可变和不可变),但到目前为止我所缺少的是hashCode
和equals
。虽然列表没有问题,因为它们是有序集合,但它们对集合和地图起着特殊的作用。如果两个集合具有相同的大小和相同的元素,则它们被认为是相等的,并且集合维护它们的顺序不应该在它们的相等性上有所不同。由于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返回相同的值?
答案 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一样长。因为您正在对位进行异或,所以无论元素的顺序如何,哈希码都将是相同的。但是,与任何哈希实现一样,将有可能发生冲突。