Java Arrays.hashCode的碰撞强度

时间:2019-09-04 09:01:06

标签: java arrays hash

Arrays.hashCode方法中使用的哈希机制对冲突有多强?用这些方法计算出的两个不同数组(例如double)具有确切的哈希值的可能性是什么?

2 个答案:

答案 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位作为黑色。我敢打赌,您会看到一个非常规则的模式。