Es un tipo de cadena con respecto interruptor numérico y siempre compilado para lookupswitch?

Lucio:

Los siguientes rendimientos de código si un dado String ses igual a cualquier otra de las cuerdas no modificables. El método utiliza el switch-statement para hacerlo:

public class SwitchOnString {
    public static boolean equalsAny(String s) {
        switch (s) {
        case "string 1":
            return true;
        case "string 2":
            return true;
        default:
            return false;
        }
    }
}

De acuerdo con la Máquina Virtual de Java Especificación (JMS) 3.10 Compilación Cambia "

Recopilación de sentencias switch utiliza los tableswitch y lookupswitch instrucciones.

Además

tableswitch y lookupswitch instrucciones operan únicamente en intlos datos.

He leído el capítulo 3.10, pero no encontró ningún Stringmencionado.

El sólo una frase que viene indirectamente cerca es:

Otros tipos numéricos deben ser reducidas a tipo int para su uso en un interruptor.

Pregunta 1:
¿Es Stringen este contexto también un tipo numérico? Hice o me he perdido algo?

Una javap -cde las clases SwitchOnStringespectáculos:

Compiled from "SwitchOnString.java"
public class playground.SwitchOnString {
  public playground.SwitchOnString();
   ...

  public static boolean equalsAny(java.lang.String);
    Code:
       0: aload_0
       1: dup
       2: astore_1
       3: invokevirtual #16                 // Method java/lang/String.hashCode:()I
       6: lookupswitch  { // 2
            1117855161: 32
            1117855162: 44
               default: 60
          }
   ...

}

Es evidente que los hashCodevalores se utilizan como intteclas situadas de los cases. Esto probablemente coincide:

Los lookupswitch pares de instrucciones intclaves (los valores de las etiquetas de caso) ...

Pasando a tableswitch y lookupswitch las JMS dice:

El tableswitch instrucción se utiliza cuando los casos del interruptor se pueden representar eficientemente como índices en una tabla de compensaciones de destino. (...) Cuando los casos del interruptor son escasos, la representación de la tabla de la tableswitch instrucción se vuelve ineficiente en términos de espacio. El lookupswitch instrucción puede ser utilizado en su lugar.

Si consigo este derecho, entonces el más los casos se vuelven escasos, más probablemente el lookupswitch será utilizado.

Pregunta 2:
pero mirando el código de bytes:
dos casos de cuerda lo suficientemente escasa para compilar switcha lookupswitch ? ¿O cada interruptor en Stringser compilado para lookupswitch ?

Holger:

La especificación no dice cómo compilar switchlas declaraciones, que es hasta el compilador.

En ese sentido, la declaración JVM, “Otros tipos numéricos debe reducirse a escribir intpara su uso en un switch” no dice que el lenguaje de programación Java va a hacer tal conversión ni que Stringo Enumson tipos numéricos. Es decir long, floaty double son tipos numéricos, pero no hay soporte para el uso con switchlas declaraciones en el lenguaje de programación Java.

Por lo que el lenguaje de especificación dice que switchmás Stringse apoya, por lo tanto, los compiladores deben encontrar una manera de compilar a código de bytes. El uso de una propiedad invariante como el código hash es una solución común, pero, en principio, otras propiedades como la longitud o un carácter arbitrario podría ser utilizado también.

Como se discutió en “ ¿Por qué cambiar de cadena compila en dos interruptores ” y “ interruptor de cadena de Java 7 decompiled: instrucción inesperada ”, javacgenera actualmente dos instrucciones de conmutación en el nivel de código de bytes cuando se compilan switchsobre Stringlos valores (TJE también genera dos instrucciones, pero los detalles pueden ser diferentes) .

A continuación, el compilador tiene que escoger bien, una lookupswitcho tableswitchinstrucción. javachace uso tableswitchcuando los números no son escasos, pero sólo si la instrucción tiene más de dos etiquetas de caso.

Así que cuando compilo el siguiente método:

public static char two(String s) {
    switch(s) {
        case "a": return 'a';
        case "b": return 'b';
    }
    return 0;
}

yo obtengo

public static char two(java.lang.String);
Code:
   0: aload_0
   1: astore_1
   2: iconst_m1
   3: istore_2
   4: aload_1
   5: invokevirtual #9                  // Method java/lang/String.hashCode:()I
   8: lookupswitch  { // 2
                97: 36
                98: 50
           default: 61
      }
  36: aload_1
  37: ldc           #10                 // String a
  39: invokevirtual #11                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  42: ifeq          61
  45: iconst_0
  46: istore_2
  47: goto          61
  50: aload_1
  51: ldc           #12                 // String b
  53: invokevirtual #11                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  56: ifeq          61
  59: iconst_1
  60: istore_2
  61: iload_2
  62: lookupswitch  { // 2
                 0: 88
                 1: 91
           default: 94
      }
  88: bipush        97
  90: ireturn
  91: bipush        98
  93: ireturn
  94: iconst_0
  95: ireturn

pero cuando compilo,

public static char three(String s) {
    switch(s) {
        case "a": return 'a';
        case "b": return 'b';
        case "c": return 'c';
    }
    return 0;
}

yo obtengo

public static char three(java.lang.String);
Code:
   0: aload_0
   1: astore_1
   2: iconst_m1
   3: istore_2
   4: aload_1
   5: invokevirtual #9                  // Method java/lang/String.hashCode:()I
   8: tableswitch   { // 97 to 99
                97: 36
                98: 50
                99: 64
           default: 75
      }
  36: aload_1
  37: ldc           #10                 // String a
  39: invokevirtual #11                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  42: ifeq          75
  45: iconst_0
  46: istore_2
  47: goto          75
  50: aload_1
  51: ldc           #12                 // String b
  53: invokevirtual #11                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  56: ifeq          75
  59: iconst_1
  60: istore_2
  61: goto          75
  64: aload_1
  65: ldc           #13                 // String c
  67: invokevirtual #11                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  70: ifeq          75
  73: iconst_2
  74: istore_2
  75: iload_2
  76: tableswitch   { // 0 to 2
                 0: 104
                 1: 107
                 2: 110
           default: 113
      }
 104: bipush        97
 106: ireturn
 107: bipush        98
 109: ireturn
 110: bipush        99
 112: ireturn
 113: iconst_0
 114: ireturn

No está claro por qué javachace esta elección. Si bien tableswitchtiene una mayor huella de base (una palabra adicional 32 bits) en comparación con lookupswitch, todavía sería más corta en bytecode, incluso para el dos caseescenario etiquetas.

Pero la consistencia de la decisión puede ser demostrado con la segunda declaración, que siempre tendrá el mismo rango de valores, pero se compila en lookupswitcho tableswitchque sólo depende de la cantidad de etiquetas. Así que cuando se utilizan los valores verdaderamente escasas:

public static char three(String s) {
    switch(s) {
        case "a": return 'a';
        case "b": return 'b';
        case "": return 0;
    }
    return 0;
}

compila a

public static char three(java.lang.String);
Code:
   0: aload_0
   1: astore_1
   2: iconst_m1
   3: istore_2
   4: aload_1
   5: invokevirtual #9                  // Method java/lang/String.hashCode:()I
   8: lookupswitch  { // 3
                 0: 72
                97: 44
                98: 58
           default: 83
      }
  44: aload_1
  45: ldc           #10                 // String a
  47: invokevirtual #11                 // 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           #12                 // String b
  61: invokevirtual #11                 // 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           #13                 // String
  75: invokevirtual #11                 // 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: 118
           default: 120
      }
 112: bipush        97
 114: ireturn
 115: bipush        98
 117: ireturn
 118: iconst_0
 119: ireturn
 120: iconst_0
 121: ireturn

el uso lookupswitchde los códigos hash escaso, pero tableswitchpara el segundo interruptor.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=191541&siteId=1
Recomendado
Clasificación