Czytam JVM specification o kompilowaniu przełączników i zainteresowałem się sposobem kompilacji instrukcji switch na łańcuchu. Oto metoda badawcza Zbadałem (JDK1.7.0_40):Po co włączać kompilacje String na dwa przełączniki?
static int test(String i) {
switch (i) {
case "a": return -100;
case "45b": return 1;
case "c": return 2;
default: return -1;
}
}
Spodziewam ten sposób być zestawiane w prosty lookupswitch na hashcode sznurka, ale nagle
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
Jak można zobaczyć, w gałęziach pierwszego przeszukiwania JVM nie wykonuje zamiast tego rzeczywistych wskaźników generujących pracę dla kolejnego przełącznika tablicowego (linia 84).
Przełącznik powinien działać szybko, więc nie przynosi dużo dodatkowej pracy. Ale w każdym razie, jaki jest cel generowania dodatkowego przełącznika?
Aktualizacja
Rozumiem możliwość kolizji hashCode. Próbuję powiedzieć, że zamiast następnego przełącznika tabelowego, kompilator może przenieść całą rzeczywistą pracę z kolejnego przełącznika tablicowego na pierwszy, a następnie użyć ifeq, aby przeskoczyć na koniec wszystkich gałęzi przełącznika. Tak więc jedna możliwa odpowiedź widzę tutaj: że w pierwszym kompilatorze przełączającym próbuje się wstępnie obliczyć etykietę dla ifeq skakać na podstawie znanej liczby przypadków, ale nie jestem pewien, że jest to jedyny powód.
Update2
Jak @ericbn zasugerował, próbowałem skompilować
switch (i) {
case 97: return -100;
case 51713: return 1;
case 99: return 2;
default: return -1;
}
z I jako int i kompilator dał mi zwykły lookupswitch.
Wyraźny String (s) może mieć taki sam hashCode ze względu na zasady zaszufladkować. Myślę, że dlatego robi dwa przełączniki. –
nie sądzisz, że istnieje tylko jeden przełącznik? drugi przełącznik służy do warunkowego powrotu. – HuStmpHrrr
@HuStmpHrrr Drugi przełącznik wprowadza pozornie pozbawioną motywacji warstwę pośrednią, skoki podnoszone od stołu mogły zostać włożone bezpośrednio do pierwszego bloku przełączającego jako cele 'goto'. –