Was bedeutet Doppel-Tilde (~~) in Java?

192

Beim Durchsuchen des Quellcodes von Guava stieß ich auf den folgenden Code (Teil der Implementierung von hashCodefür die innere Klasse 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;

Beide von adjustund hashsind ints. Soweit ich über Java weiß, ~bedeutet dies bitweise Negation adjust = ~~adjustund hash = ~~hashsollte die Variablen unverändert lassen. Ausführen des kleinen Tests (mit aktivierten Zusicherungen natürlich),

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

bestätigt dies. Vorausgesetzt, die Guaven wissen, was sie tun, muss es einen Grund für sie geben, dies zu tun. Die Frage ist was?

BEARBEITEN Wie in den Kommentaren ausgeführt, enthält der obige Test nicht den Fall, in dem igleich ist Integer.MAX_VALUE. Da dies i <= Integer.MAX_VALUEimmer der Fall ist, müssen wir diesen Fall außerhalb der Schleife überprüfen, um zu verhindern, dass er für immer wiederholt wird. Allerdings ist die Linie

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

gibt die Compiler-Warnung "Vergleichen identischer Ausdrücke" aus, die es so ziemlich nagelt.

Halle Knast
quelle
42
@dr_andonuts Guava ist heutzutage eine ziemlich Standardbibliothek, die in ein Projekt aufgenommen werden soll. Ich denke, der Rat, weit wegzulaufen, ist falsch.
Yshavit
4
Die Zusicherung überprüft den Kantenfall nicht Integer.MAX_VALUE. Kontrast zu -(-Integer.MIN_VALUE) != Integer.MIN_VALUE.
Franky
3
@ Franky Was Maaartinus gesagt hat. -Integer.MIN_VALUEwickelt sich um Integer.MIN_VALUE, so negiert das wieder einfach Integer.MIN_VALUEwieder.
2
@maaartinus, @hvd, danke, dass du darauf hingewiesen hast. Jetzt erinnere ich mich daran -x = (~x) + 1.
Franky
7
@dr_andonuts Trolling? Warum vor Dingen davonlaufen, die du nicht verstehst? Dafür ist StackOverflow da: um Ihnen beim Lernen zu helfen.
Jared Burrows

Antworten:

245

In Java bedeutet es nichts.

Dieser Kommentar besagt jedoch, dass die Zeile speziell für GWT bestimmt ist. Dies ist eine Möglichkeit, Java zu JavaScript zu kompilieren.

In JavaScript sind Ganzzahlen wie Doppelzahlen, die als Ganzzahlen fungieren. Sie haben zum Beispiel einen Maximalwert von 2 ^ 53. Aber Bitoperatoren behandelt Zahlen , als ob sie 32-Bit sind, das ist genau das , was Sie in diesem Code werden sollen. Mit anderen Worten, ~~hashsagt " hashAls 32-Bit-Zahl behandeln" in JavaScript. Insbesondere werden alle bis auf die unteren 32 Bits verworfen (da die bitweisen ~Operatoren nur die unteren 32 Bits betrachten), was mit der Funktionsweise des Java-Überlaufs identisch ist.

Wenn Sie das nicht hätten, wäre der Hash-Code des Objekts unterschiedlich, je nachdem, ob es in Java-Land oder in JavaScript-Land (über eine GWT-Kompilierung) ausgewertet wird.

yshavit
quelle
10
@harold Es ist keine GWT-Sache, es ist eine JavaScript-Sache. Das ist gerade so, dass Zahlen in dieser Sprache funktionieren.
Yshavit
18
@yshavit Es ist jedoch nicht so, wie Zahlen in Java funktionieren. Wenn GWT die Tatsache nicht verbirgt, dass Zahlen in JS und in der JVM anders implementiert sind als der Benutzer, ist es in der Tat ein schlechter Compiler.
Valderman
8
@harold, ja, es ist JavaScript, das Ganzzahlen falsch implementiert (in JavaScript gibt es eigentlich keinen Ganzzahltyp).
MikeTheLiar
8
@valderman Das ist ein guter Punkt. Das Hinzufügen von |0oder ~~klingt so, als wäre es nicht schwer, obwohl ich nicht weiß, wie der Performance-Hit aussehen würde (Sie müssten es bei jedem Schritt jedes Ausdrucks hinzufügen). Ich weiß nicht, was die Designüberlegungen waren. Fwiw, die Inkonsistenz ist auf der GWT-Kompatibilitätsseite dokumentiert .
Yshavit
6
hashCodeist insofern seltsam, als es absichtlich einen Überlauf umwirbt oder sogar erwartet. Der einzige Ort, an dem Sie Inkonsistenzen beobachten können, ist der Ort, an dem ein normaler Java-Int überlaufen würde. Dies ist kein Problem, das im meisten Code auftritt. es ist nur in diesem einen seltsamen Fall relevant.
Louis Wasserman