I JVM specification zum Kompilieren Schalter lesen und wurde interessiert, wie switch-Anweisung auf String zusammengestellt. Hier ist die Testmethode I geprüft (JDK1.7.0_40):Warum Schalter auf String kompiliert in zwei Schalter
static int test(String i) {
switch (i) {
case "a": return -100;
case "45b": return 1;
case "c": return 2;
default: return -1;
}
}
ich diese Methode erwarten zusammengestellt in einfache lookupswitch auf hashCode Schnur an, aber plötzlich
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
Wie man sehen konnte, in Zweigen des ersten Nachschlagewerkes macht JVM nicht die wirkliche Arbeit, stattdessen Indizes für den nachfolgenden Tabellenschalter (Zeile 84) zu erzeugen.
TableSwitch sollte schnell arbeiten, bringt also nicht viel zusätzliche Arbeit. Aber was ist der Zweck, zusätzliche Schalter zu generieren?
Update
Ich verstehe die Möglichkeit von HashCode Kollisionen. Was ich zu sagen versuche, ist, dass der Compiler anstelle des nachfolgenden Tablewitch die gesamte echte Arbeit aus dem nachfolgenden tableswitch in den ersten verschieben und ifeq dann verwenden kann, um zum Ende aller Switch-Zweige zu springen. So die eine mögliche Antwort sehe ich hier: dass in ersten Switch Compiler versucht, Etikett für ifeq Sprung basierend auf bekannten Anzahl von Fällen vorberechnen, aber ich bin mir nicht sicher, dass es der einzige Grund ist.
Update2
Als @ericbn vorgeschlagen, versuchte ich
switch (i) {
case 97: return -100;
case 51713: return 1;
case 99: return 2;
default: return -1;
}
mit i zu kompilieren als int und Compiler mir schlicht lookupswitch gab.
Distinct Saite (n) die gleiche hashCode aufgrund der Schubfachprinzip haben. Ich denke, deshalb gibt es zwei Schalter. –
Glaubst du nicht, dass es nur einen Schalter gibt? der zweite Schalter ist für die bedingte Rückkehr. – HuStmpHrrr
@HuStmpHrrr Der zweite Schalter führt eine scheinbar unmotivierte Dereferenzierungsschicht ein, die Sprünge, die von der Tabelle nachgeschlagen wurden, könnten direkt in den ersten Schalterblock als "Goto" -Ziele eingefügt worden sein. –