В чем смысл двойной тильды (~~) в Java?

При просмотре исходного кода Guava я наткнулся на следующий фрагмент кода (часть реализации 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 и hash равны int s. Из того, что я знаю о Java, ~ означает побитовое отрицание, поэтому adjust = ~~adjust и hash = ~~hash должны оставить переменные неизменными. Выполнение небольшого теста (с уверенностью разрешено, конечно),

for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
    assert i == ~~i;
}

подтверждает это. Предполагая, что ребята из Guava знают, что они делают, для этого должна быть причина. Вопрос в том, что?

EDIT Как указано в комментариях, вышеприведенный тест не включает случай, когда i равно Integer.MAX_VALUE. Поскольку i <= Integer.MAX_VALUE всегда истинно, нам нужно будет проверить этот случай вне цикла, чтобы он не зацикливался навсегда. Однако линия

assert Integer.MAX_VALUE == ~~Integer.MAX_VALUE;

дает предупреждение компилятора "Сравнение идентичных выражений", которое в значительной степени прибивает его.

Ответ 1

В Java это ничего не значит.

Но в этом комментарии говорится, что строка специально предназначена для GWT, что является способом компиляции Java на JavaScript.

В JavaScript целые числа являются похожими на doubles-a-act-as-integers. Например, они имеют максимальное значение 2 ^ 53. Но побитовые операторы обрабатывают номера так, как если бы они были 32-битными, что именно то, что вы хотите в этом коде. Другими словами, ~~hash говорит "лечить hash как 32-разрядное число" в JavaScript. В частности, он отбрасывает все, кроме нижних 32 бита (поскольку побитовые операторы ~ смотрят только на нижние 32 бита), что идентично тому, как работает переполнение Java.

Если у вас этого нет, хэш-код объекта будет отличаться в зависимости от того, была ли она оценена на Java-земле или на земле JavaScript (с помощью компиляции GWT).