¿Cuál es el significado de doble tilde (~~) en Java?

192

Al examinar el código fuente de Guava, me encontré con el siguiente código (parte de la implementación de hashCodela clase interna 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;

Ambos adjusty hashson ints. Por lo que sé acerca de Java, ~mediante la negación bit a bit, por lo que adjust = ~~adjust, y hash = ~~hashdebería salir de las variables sin cambios. Ejecutando la pequeña prueba (con aserciones habilitadas, por supuesto),

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

confirma esto Suponiendo que los chicos de la guayaba saben lo que están haciendo, debe haber una razón para que lo hagan. La pregunta es qué?

EDITAR Como se señaló en los comentarios, la prueba anterior no incluye el caso donde ies igual Integer.MAX_VALUE. Como i <= Integer.MAX_VALUEsiempre es cierto, tendremos que verificar ese caso fuera del bucle para evitar que se repita para siempre. Sin embargo, la linea

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

produce la advertencia del compilador "Comparación de expresiones idénticas", que prácticamente lo clava.

Halle Knast
fuente
42
@dr_andonuts Guava es una biblioteca bastante estándar para incluir en un proyecto en estos días; creo que el consejo de correr lejos está fuera de lugar.
yshavit
44
La afirmación no verifica el caso límite Integer.MAX_VALUE. Contraste con -(-Integer.MIN_VALUE) != Integer.MIN_VALUE.
Franky
3
@Franky Lo que dijo maaartinus. -Integer.MIN_VALUEse envuelve Integer.MIN_VALUE, negando eso nuevamente, simplemente produce Integer.MIN_VALUEnuevamente.
2
@maaartinus, @hvd, gracias por señalarlo. Ahora lo recuerdo -x = (~x) + 1.
Franky
77
@dr_andonuts Trolling? ¿Por qué huir de cosas que no entiendes? Para eso está StackOverflow: para ayudarlo a aprender.
Jared Burrows

Respuestas:

245

En Java, no significa nada.

Pero ese comentario dice que la línea es específicamente para GWT, que es una forma de compilar Java a JavaScript.

En JavaScript, los enteros son como dobles-que-actúan como enteros. Tienen un valor máximo de 2 ^ 53, por ejemplo. Pero los operadores bit a bit tratan los números como si fueran de 32 bits, que es exactamente lo que quieres en este código. En otras palabras, ~~hashdice "tratar hashcomo un número de 32 bits" en JavaScript. Específicamente, descarta todos menos los 32 bits inferiores (dado que los ~operadores bit a bit solo miran los 32 bits inferiores), que es idéntico a cómo funciona el desbordamiento de Java.

Si no tuviera eso, el código hash del objeto sería diferente dependiendo de si se evalúa en Java-Land o en JavaScript Land (a través de una compilación GWT).

yshavit
fuente
10
@harold No es una cosa de GWT, es una cosa de JavaScript. Eso es ahora, los números funcionan en ese idioma.
yshavit
18
@yshavit Sin embargo, no es así como funcionan los números en Java. Si GWT no oculta el hecho de que los números se implementan de manera diferente en JS y en la JVM del usuario, entonces es un compilador deficiente.
valderman
8
@harold, sí, es JavaScript el que implementa enteros incorrectamente (en realidad no existe un tipo entero en JavaScript).
MikeTheLiar
8
@valderman Ese es un buen punto. Agregar |0o ~~suena como si no fuera difícil, aunque no sé cuál sería el éxito en el rendimiento (tendría que agregarlo en cada paso de cada expresión). No sé cuáles fueron las consideraciones de diseño. Fwiw, la inconsistencia está documentada en la página de compatibilidad de GWT .
yshavit
66
hashCodees extraño porque deliberadamente corteja, o incluso espera, que ocurra un desbordamiento. El único lugar donde puede observar inconsistencias es donde se desbordaría un int Java normal, que no es un problema que aparece en la mayoría del código; es relevante en este caso extraño.
Louis Wasserman