在浏览 Guava 的源代码时,我遇到了以下一段代码(hashCode内部类的部分实现CartesianSet):
hashCode
CartesianSet
int adjust = size() - 1; for (int i = 0; i < axes.size(); i++) { adjust *= 31; adjust = ~~adjust; // in GWT, we have to deal with integer overflow carefully } int hash = 1; for (Set<E> axis : axes) { hash = 31 * hash + (size() / axis.size() * axis.hashCode()); hash = ~~hash; } hash += adjust; return ~~hash;
adjust和都是s hash。int根据我对 Java 的了解,~这意味着按位否定,因此adjust = ~~adjust应该hash = ~~hash保持变量不变。运行小测试(当然,启用断言),
adjust
hash
int
~
adjust = ~~adjust
hash = ~~hash
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { assert i == ~~i; }
证实了这一点。假设番石榴家伙知道他们在做什么,那么他们这样做肯定是有原因的。问题是什么?
编辑 正如评论中指出的那样,上面的测试不包括iequals的情况Integer.MAX_VALUE。由于i <= Integer.MAX_VALUE总是正确的,我们需要在循环外检查这种情况,以防止它永远循环。然而,线
i
Integer.MAX_VALUE
i <= Integer.MAX_VALUE
assert Integer.MAX_VALUE == ~~Integer.MAX_VALUE;
产生编译器警告“比较相同的表达式”,这几乎可以说明问题。
在 Java 中,它没有任何意义。
但是该评论说该行专门针对 GWT,这是一种将 Java 编译为 JavaScript 的方法。
在 JavaScript 中,整数有点像 doubles-that-act-as-integers。例如,它们的最大值为 2^53。但是按位运算符将数字视为 32 位,这正是您在此代码中想要的。换句话说,在 JavaScript 中~~hash表示“hash视为 32 位数字”。具体来说,它会丢弃除底部 32 位之外的所有位(因为按位运算~符只查看底部 32 位),这与 Java 溢出的工作方式相同。
~~hash
如果你没有,对象的哈希码会有所不同,具体取决于它是在 Java 领域还是在 JavaScript 领域(通过 GWT 编译)进行评估。