poner borde java jvm switch-statement java-7 bytecode

java - borde - ¿Por qué encender la cadena compila en dos interruptores?



text panel java (1)

Leí la especificación de JVM al compilar los conmutadores y me interesé en cómo se compila la instrucción de conmutador en String. Aquí está el método de prueba que examiné (JDK1.7.0_40):

static int test(String i) { switch (i) { case "a": return -100; case "45b": return 1; case "c": return 2; default: return -1; } }

Espero que este método se compile en un simple interruptor de búsqueda en el código hash de la cadena, pero de repente

static int test(java.lang.String); Code: 0: aload_0 1: astore_1 2: iconst_m1 3: istore_2 4: aload_1 5: invokevirtual #6 // Method java/lang/String.hashCode:()I 8: lookupswitch { // 3 97: 44 99: 72 51713: 58 default: 83 } 44: aload_1 45: ldc #7 // String a 47: invokevirtual #8 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 50: ifeq 83 53: iconst_0 54: istore_2 55: goto 83 58: aload_1 59: ldc #9 // String 45b 61: invokevirtual #8 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 64: ifeq 83 67: iconst_1 68: istore_2 69: goto 83 72: aload_1 73: ldc #10 // String c 75: invokevirtual #8 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 78: ifeq 83 81: iconst_2 82: istore_2 83: iload_2 84: tableswitch { // 0 to 2 0: 112 1: 115 2: 117 default: 119 } 112: bipush -100 114: ireturn 115: iconst_1 116: ireturn 117: iconst_2 118: ireturn 119: iconst_m1 120: ireturn

Como puede ver, en las ramas del primer buscador JVM no está haciendo el trabajo real generando índices para el siguiente interruptor de tabla (línea 84).
Tableswitch debería trabajar rápido para que no traiga mucho trabajo extra. Pero de todos modos, ¿cuál es el propósito de generar un switch adicional?
Actualizar
Entiendo la posibilidad de colisiones hashCode. Lo que estoy tratando de decir es que, en lugar de los conmutadores de tablas subsiguientes, el compilador podría mover todo el trabajo real del conmutador de tablas subsiguientes al primero y luego usar ifeq para saltar al final de todas las ramas de switch. Entonces, la única respuesta posible que veo aquí es que, en el primer compilador, el conmutador intenta calcular previamente la etiqueta para el salto ifeq en función de un número conocido de casos, pero no estoy seguro de que sea la única razón.

Actualización2
Como @ericbn sugirió, traté de compilar

switch (i) { case 97: return -100; case 51713: return 1; case 99: return 2; default: return -1; }

con i como int y el compilador me dio un simple lookupswitch.


La cita del código fuente de javac :

* The general approach used is to translate a single * string switch statement into a series of two chained * switch statements: the first a synthesized statement * switching on the argument string''s hash value and * computing a string''s position in the list of original * case labels, if any, followed by a second switch on the * computed integer value. The second switch has the same * code structure as the original string switch statement * except that the string case labels are replaced with * positional integer constants starting at 0. * * The first switch statement can be thought of as an * inlined map from strings to their position in the case * label list. An alternate implementation would use an * actual Map for this purpose, as done for enum switches. * * With some additional effort, it would be possible to * use a single switch statement on the hash code of the * argument, but care would need to be taken to preserve * the proper control flow in the presence of hash * collisions and other complications, such as * fallthroughs. Switch statements with one or two * alternatives could also be specially translated into * if-then statements to omit the computation of the hash * code.