Przeglądając kod źródłowy Guava natrafiłem na następujący fragment kodu (część implementacji hashCode
klasy wewnętrznej 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;
Oba są adjust
i hash
są int
. Z tego, co wiem o Javie, ~
oznacza bitową negację, więc adjust = ~~adjust
i hash = ~~hash
powinienem pozostawić zmienne bez zmian. Uruchomienie małego testu (oczywiście z włączonymi asercjami),
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
assert i == ~~i;
}
potwierdza to. Zakładając, że Guava wiedzą, co robią, musi istnieć powód, aby to zrobić. Pytanie brzmi: co?
EDYCJA Jak wskazano w komentarzach, powyższy test nie obejmuje przypadku, w którym i
jest równy Integer.MAX_VALUE
. Ponieważ i <= Integer.MAX_VALUE
zawsze jest to prawdą, musimy sprawdzić tę skrzynkę poza pętlą, aby zapobiec jej zapętleniu na zawsze. Jednak linia
assert Integer.MAX_VALUE == ~~Integer.MAX_VALUE;
wyświetla ostrzeżenie kompilatora „Porównywanie identycznych wyrażeń”, które w znacznym stopniu go dotyka.
źródło
Integer.MAX_VALUE
. Porównaj z-(-Integer.MIN_VALUE) != Integer.MIN_VALUE
.-Integer.MIN_VALUE
owija się wokółInteger.MIN_VALUE
, tak negując, że znów po prostu produkujeInteger.MIN_VALUE
ponownie.-x = (~x) + 1
.Odpowiedzi:
W Javie to nic nie znaczy.
Ale ten komentarz mówi, że wiersz jest specjalnie dla GWT, który jest sposobem na kompilację Java do JavaScript.
W JavaScript, liczby całkowite są jakby liczbami podwójnymi, które działają jak liczby całkowite. Na przykład mają maksymalną wartość 2 ^ 53. Ale operatory bitowe traktują liczby tak, jakby były 32-bitowe, co jest dokładnie tym, czego chcesz w tym kodzie. Innymi słowy, w JavaScript
~~hash
mówi „traktujhash
jak liczbę 32-bitową”. W szczególności odrzuca wszystkie oprócz dolnych 32 bitów (ponieważ~
operatory bitowe spoglądają tylko na dolne 32 bity), co jest identyczne z tym, jak działa przepełnienie Javy.Jeśli tego nie masz, kod skrótu obiektu byłby różny w zależności od tego, czy jest on oceniany w Javie, czy w JavaScript (za pomocą kompilacji GWT).
źródło
|0
lub~~
brzmi, jakby to nie było trudne, choć nie wiem, jaki byłby hit wydajności (trzeba by to dodawać na każdym etapie każdego wyrażenia). Nie wiem, jakie były względy projektowe. Fwiw, niespójność jest udokumentowana na stronie kompatybilności GWT .hashCode
jest dziwne, ponieważ celowo sądzi, a nawet spodziewa się, że nastąpi przepełnienie. Jedynym miejscem, w którym można zaobserwować niespójność, jest przepełnienie normalnej int Java, co nie występuje w większości kodu; ma to znaczenie tylko w tym dziwnym przypadku.