Quelle est la signification du double tilde (~~) en Java?

192

En parcourant le code source de Guava, je suis tombé sur le morceau de code suivant (faisant partie de l'implémentation de hashCodepour la classe interne 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;

Les deux adjustet hashsont l' intart. D'après ce que je sais sur Java, ~signifie négation au niveau du bit, donc adjust = ~~adjustet hash = ~~hashdevrait laisser les variables inchangées. Lancer le petit test (avec les assertions activées, bien sûr),

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

le confirme. En supposant que les gars de Guava savent ce qu'ils font, il doit y avoir une raison pour eux de le faire. La question est quoi?

EDIT Comme indiqué dans les commentaires, le test ci-dessus n'inclut pas le cas où iest égal Integer.MAX_VALUE. Comme i <= Integer.MAX_VALUEc'est toujours vrai, nous devrons vérifier ce cas en dehors de la boucle pour l'empêcher de boucler pour toujours. Cependant, la ligne

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

renvoie l'avertissement du compilateur "Comparaison d'expressions identiques", ce qui résout le problème.

Halle Knast
la source
42
@dr_andonuts Guava est une bibliothèque assez standard à inclure dans un projet ces jours-ci - je pense que le conseil de fuir loin est déplacé.
yshavit
4
L'assertion ne vérifie pas la casse du bord Integer.MAX_VALUE. Contraste avec -(-Integer.MIN_VALUE) != Integer.MIN_VALUE.
Franky
3
@Franky Ce que maaartinus a dit. -Integer.MIN_VALUEs'enroule autour de Integer.MIN_VALUE, donc nier que de nouveau produit simplement à Integer.MIN_VALUEnouveau.
2
@maaartinus, @hvd, merci de l'avoir signalé. Maintenant je m'en souviens -x = (~x) + 1.
Franky
7
@dr_andonuts Trolling? Pourquoi fuir les choses que vous ne comprenez pas. C'est pour cela que StackOverflow est là: pour vous aider à apprendre.
Jared Burrows

Réponses:

245

En Java, cela ne veut rien dire.

Mais ce commentaire dit que la ligne est spécifiquement pour GWT, qui est un moyen de compiler Java en JavaScript.

En JavaScript, les entiers sont un peu comme des doubles qui agissent comme des entiers. Ils ont une valeur maximale de 2 ^ 53, par exemple. Mais opérateurs au niveau du bit traitent les nombres comme s'ils étaient 32 bits, ce qui est exactement ce que vous voulez dans ce code. En d'autres termes, ~~hashdit "traiter hashcomme un nombre 32 bits" en JavaScript. Plus précisément, il supprime tous sauf les 32 bits inférieurs (puisque les ~opérateurs au niveau du bit ne regardent que les 32 bits inférieurs), ce qui est identique au fonctionnement du débordement de Java.

Si vous ne l'aviez pas, le code de hachage de l'objet serait différent selon qu'il est évalué en Java-land ou en JavaScript land (via une compilation GWT).

yshavit
la source
10
@harold Ce n'est pas un truc GWT, c'est un truc JavaScript. C'est juste maintenant que les chiffres fonctionnent dans cette langue.
yshavit
18
@yshavit Cependant, ce n'est pas ainsi que les nombres fonctionnent en Java. Si GWT ne cache pas le fait que les nombres sont implémentés différemment dans JS et sur la JVM de l'utilisateur, alors c'est en effet un mauvais compilateur.
valderman
8
@harold, ouais, c'est JavaScript qui implémente les entiers de manière incorrecte (il n'y a en fait pas de type entier en JavaScript).
MikeTheLiar
8
@valderman C'est un bon point. Ajouter |0ou ~~semble ne pas être difficile, même si je ne sais pas quel serait le succès de la performance (vous devrez l'ajouter à chaque étape de chaque expression). Je ne sais pas quelles étaient les considérations de conception. Fwiw, l'incohérence est documentée sur la page de compatibilité de GWT .
yshavit
6
hashCodeest étrange dans la mesure où elle courtise délibérément , voire s'attend à ce qu'un débordement se produise. Le seul endroit où vous pouvez observer une incohérence est l'endroit où un int Java normal déborderait, ce qui n'est pas un problème qui se pose dans la plupart du code; c'est juste pertinent dans ce cas étrange.
Louis Wasserman