Arrays.hashCode
方法中使用的哈希机制对冲突有多强?用这些方法计算出的两个不同数组(例如double
)具有确切的哈希值的可能性是什么?
答案 0 :(得分:2)
Arrays.hashCode(double[])
被指定为返回List
的等效值,其中包含表示相同数值的Double
个值。
List.hashCode
依次使用相当简单的算法指定:
int hashCode = 1;
for (E e : list)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
通常,将质数与乘法相乘是通用哈希函数的一种好习惯,但它与具有强大密码学功能的哈希函数相去甚远。
这意味着虽然在一般情况下(实际上是随机的)碰撞不太可能发生,但是如果您可以影响(或选择)项目的hashCode
,通常可以很容易地构造它们 在列表中。
作为构造示例,请考虑以下两个语句:
System.out.println(Arrays.hashCode(new double[] {4.753E-321d}));
System.out.println(Arrays.hashCode(new double[] {4.9E-324d, 4.9E-324d}));
尽管数组明显不同,但两者都将输出993。
答案 1 :(得分:0)
这是您使用的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
enter code here
println(hashes.size)//打印25 println(hashes.toSet.size)//打印25 要验证数字是否发生这种情况,可以按如下方式创建图形:对于每个哈希,将x的前16位和y的后16位作为黑色。我敢打赌,您会看到一个非常规则的模式。