Яке значення подвійного тильду (~~) у Java?


192

Під час перегляду вихідного коду 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с. З того, що я знаю про Java, ~означає побітове заперечення, тому adjust = ~~adjustі hash = ~~hashслід залишати змінні незмінними. Запустивши невеликий тест (звичайно з увімкненими твердженнями),

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

це підтверджує. Якщо припустити, що хлопці з Гуави знають, що вони роблять, у них повинна бути причина. Питання - що?

EDIT Як зазначено в коментарях, тест, зазначений вище, не включає випадок, коли iдорівнює Integer.MAX_VALUE. Оскільки i <= Integer.MAX_VALUEце завжди вірно, нам потрібно буде перевірити цей випадок за межами циклу, щоб не допустити його циклу назавжди. Однак лінія

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

дає попередження компілятора "Порівняння однакових виразів", що в значній мірі нівелює.


42
@dr_andonuts Guava - це досить стандартна бібліотека, яку потрібно включити в проект сьогодні - я вважаю, що пора бігти далеко не так.
yshavit

4
Утвердження не перевіряє крайній регістр Integer.MAX_VALUE. Контраст с -(-Integer.MIN_VALUE) != Integer.MIN_VALUE.
Френкі

3
@Franky Що сказав maaartinus. -Integer.MIN_VALUEобгортає Integer.MIN_VALUE, настільки заперечуючи, що знову просто Integer.MIN_VALUEзнову створюється .

2
@maaartinus, @hvd, дякую, що вказав на це. Тепер я це пам’ятаю -x = (~x) + 1.
Френкі

7
@dr_andonuts Тролінг? Навіщо тікати від речей, яких ти не розумієш. Ось для чого тут StackOverflow: щоб допомогти вам навчитися.
Джаред Берроуз

Відповіді:


245

На Java це нічого не означає.

Але цей коментар говорить, що ця лінія спеціально для GWT, що є способом компілювати Java в JavaScript.

У JavaScript цілі числа є схожими на подвійні числа-що-діють-як-цілі числа. Наприклад, вони мають максимальне значення 2 ^ 53. Але побітові оператори розглядають числа так, ніби вони 32-бітні, що саме те, що ви хочете в цьому коді. Іншими словами, в JavaScript ~~hashнаписано "ставитися hashяк до 32-розрядного числа". Зокрема, він відкидає всі, крім нижнього 32 біта (оскільки побітові ~оператори дивляться лише на нижній 32 біт), що ідентично тому, як працює переповнення Java.

Якщо у вас цього не було, хеш-код об'єкта відрізнявся б від того, оцінюється він у Java-land або JavaScript land (за допомогою компіляції GWT).


10
@harold Це не річ GWT, це річ JavaScript. Ось тільки зараз номери працюють на цій мові.
yshavit

18
@yshavit Однак, чи не так, як у Java працюють цифри. Якщо GWT не приховує того, що цифри реалізуються по-різному в JS та JVM від користувача, то це справді поганий компілятор.
valderman

8
@harold, так, це JavaScript, який неправильно реалізує цілі числа (насправді в JavaScript немає такого поняття, як цілий тип).
MikeTheLiar

8
@valderman Це хороший момент. Додавання |0або ~~звучить так, як це не буде важко, хоча я не знаю, яким би був хіт продуктивності (вам доведеться додавати його на кожному кроці кожного виразу). Я не знаю, які були дизайнерські міркування. Fwiw, невідповідність зафіксована на сторінці сумісності GWT .
yshavit

6
hashCodeдивно в тому, що воно навмисно суди, або навіть очікує, що перелив відбудеться. Єдине місце, на якому можна спостерігати непослідовність, - це те, де звичайний інт Java переповнюється, що не є проблемою, що виникає в більшості кодів; це просто актуально в цьому дивному випадку.
Луї Вассерман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.