Java hashCode可以在不同的运行之间返回不同的值吗?
我正在努力学习Java hashCode可以在不同的运行之间返回不同的值吗?,java,hash,non-deterministic,Java,Hash,Non Deterministic,我正在努力学习hashCode背后的完整故事。在大多数实现中,hashCode是完全确定的,如在StringUTF16类中: public static int hashCode(byte[] value) { int h = 0; int length = value.length >> 1; for (int i = 0; i < length; i++) { h = 31 * h + getChar(value, i); }
hashCode
背后的完整故事。在大多数实现中,hashCode
是完全确定的,如在StringUTF16
类中:
public static int hashCode(byte[] value) {
int h = 0;
int length = value.length >> 1;
for (int i = 0; i < length; i++) {
h = 31 * h + getChar(value, i);
}
return h;
}
现在,我的问题是:这个实现有什么不好的地方吗?我能看到的唯一问题是,它将为不同的程序运行返回不同的哈希代码,但我无法想象会出现错误的具体场景。除非您进入序列化的专门应用程序,否则我看不出有什么问题。在大多数情况下,就运行时而言,设置它的方式基本上相当于添加任意的
31
值(该值不变)
不过,通过反射“诡计”,您可能会改变值并使整个系统偏离轨道(想想setAccessible
和修改器标志)
当对象被序列化并传输到不同的环境时,如果存在依赖于哈希代码和一致性的设置,我认为出现问题的可能性更大。两个独立环境之间的哈希代码比较方式很可能会有所不同,但实际上它们不应该有所不同。除非您进入序列化的专门应用程序,否则我不认为这是一个很大的问题。在大多数情况下,就运行时而言,设置它的方式基本上相当于添加任意的
31
值(该值不变)
不过,通过反射“诡计”,您可能会改变值并使整个系统偏离轨道(想想setAccessible
和修改器标志)
当对象被序列化并传输到不同的环境时,如果存在依赖于哈希代码和一致性的设置,我认为出现问题的可能性更大。两个独立环境之间的哈希代码比较方式很可能会有所不同,但实际上它们不应该如此。不要求
hashCode
在不同JVM中给出相同的值。例如,序列化映射时,HashMap
类不会持久化映射键的hashCode
值。相反,反序列化映射时会重新计算hashCode
值
我能看到的唯一潜在问题是在每次调用时重新计算hashCode
,效率低下。您可以通过延迟计算来解决这个问题(例如,String::hashCode
does)
但是如果实现了lazyhashCode
计算,则需要将存储它的字段声明为transient
。否则,取消持久化密钥实例中的hashCode
值将不会=
为另一个与该密钥“相等”的实例计算的hashCode
值。(换句话说,hashcode/equals契约被破坏!)这将导致查找失败
如果正确地执行此操作,那么对HashMap
的序列化应该不会有问题。例如,您可以遵循String::hashCode
的方法,使用零作为hashCode
方法的缓存hashCode
值,这意味着“需要计算代码”
(如果您的密钥类没有用于保存缓存的hashCode
值的字段,则不会出现持久化该值的问题。)
另一件需要注意的事情是,修改key类以实现
Comparable
将是另一种防御基于DOS的攻击的方法。在示例类中,compareTo
方法的实现非常简单。请注意,您实现的顺序不需要在语义上有意义。它只需要稳定和一致。不要求hashCode
在不同的JVM中提供相同的值。例如,序列化映射时,HashMap
类不会持久化映射键的hashCode
值。相反,反序列化映射时会重新计算hashCode
值
我能看到的唯一潜在问题是在每次调用时重新计算hashCode
,效率低下。您可以通过延迟计算来解决这个问题(例如,String::hashCode
does)
但是如果实现了lazyhashCode
计算,则需要将存储它的字段声明为transient
。否则,取消持久化密钥实例中的hashCode
值将不会=
为另一个与该密钥“相等”的实例计算的hashCode
值。(换句话说,hashcode/equals契约被破坏!)这将导致查找失败
如果正确地执行此操作,那么对HashMap
的序列化应该不会有问题。例如,您可以遵循String::hashCode
的方法,使用零作为hashCode
方法的缓存hashCode
值,这意味着“需要计算代码”
(如果您的密钥类没有用于保存缓存的hashCode
值的字段,则不会出现持久化该值的问题。)
另一件需要注意的事情是,修改key类以实现
Comparable
将是另一种防御基于DOS的攻击的方法。在示例类中,compareTo
方法的实现非常简单。请注意,您实现的顺序不需要在语义上有意义。它只需要稳定和一致。考虑基于哈希的Map
<代码>哈希映射s将永远无法工作。还有,当散列的主要目的是存储和检索而不是一个目的时,您如何计划发起攻击wayness@Ryotsu,你说的是某种持久的映射吗?在这种情况下,hashCode应该是b
class ImmutableArray{
// Note static keyword. It guarantees that for the same run all objects use the same x.
private static final int x = generateRandomPrime();
int[] values;
public int hashCode() {
int res = 5;
for (int v : values) {
res = res * x + v;
}
return res;
}
...
}