我在hashmap(约280万个对象)中存储大量对象(具有存储在对象中的字节数组中的值的唯一组合),并且在检查我是否有任何哈希码冲突时(32)比特哈希),我很惊讶地看到没有统计数据,我有几乎100%的机会至少有一次碰撞(参见http://preshing.com/20110504/hash-collision-probabilities/)。
因此,我想知道我检测碰撞的方法是否被窃听,或者我是否非常幸运......
以下是我尝试检测地图中存储的280万个值的碰撞的方法:
HashMap<ShowdownFreqKeysVO, Double> values;
(...fill with 2.8 mlns unique values...)
HashSet<Integer> hashes = new HashSet<>();
for (ShowdownFreqKeysVO key:values.keySet()){
if (hashes.contains(key.hashCode())) throw new RuntimeException("Duplicate hash for:"+key);
hashes.add(key.hashCode());
}
这是对象创建哈希值的方法:
public class ShowdownFreqKeysVO {
//Values for the different parameters
public byte[] values = new byte[12];
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(values);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ShowdownFreqKeysVO other = (ShowdownFreqKeysVO) obj;
if (!Arrays.equals(values, other.values))
return false;
return true;
}
}
对于我做错了什么的任何想法/暗示都将不胜感激!
谢谢, 托马斯
答案 0 :(得分:5)
我不相信运气
这是您使用的Arrays.hashCode
的实现
public static int hashCode(int a[]) {
if (a == null)
return 0;
int result = 1;
for (int element : a)
result = 31 * result + element;
return result;
}
如果您的值恰好小于31,它们将被视为基数31中的不同数字,因此每个结果都会产生不同的数字(如果我们暂时忽略溢出)。让我们称之为纯粹的哈希
现在当然31^11
比Java中的整数数量大,所以我们会得到大量的溢出。但由于31的幂和最大整数是“非常不同”,你不会得到几乎随机的分布,而是一个非常规则的均匀分布。
让我们考虑一个较小的例子。我假设你的数组中只有2个元素,范围从0到5。我尝试通过取“纯哈希”的模38来创建0到37之间的“hashCode”。结果是我得到5个整数的条纹,中间有小间隙,而不是单个碰撞。
val hashes = for {
i <- 0 to 4
j <- 0 to 4
} yield (i * 31 + j) % 38
println(hashes.size) // prints 25
println(hashes.toSet.size) // prints 25
要验证您的号码是否会发生这种情况,您可以按如下方式创建图表: 对于每个散列,取x的前16位,y取第二个16位,点黑色。我打赌你会看到一个非常规律的模式。
答案 1 :(得分:0)
我认为您的代码没有任何问题,但您链接的分析假设hashCodes是均匀分布的,并且不同对象的hashCodes是独立的随机变量。
后者可能不是真的:您知道对象是唯一的(因此不是独立的)。也许hashCode函数保留了该特定品牌的唯一性。