2015-04-18 7 views
11

Seit Java 1.7 können Strings mit switch-Anweisungen verwendet werden, was mich etwas beunruhigt. switch Anweisungen auf Integer-Werte in Sprungtabellen umgewandelt werden, die schneller sind als einfach if prüft auf Ganzzahlen berechnet zur Laufzeit; Kann eine ähnliche Optimierung mit Strings durchgeführt werden oder ist dies nur syntaktischer Zucker?Ist Switch mit Strings mehr als nur syntaktischer Zucker?

+0

'String' Schalter scheint eine feste Größe Karte implementiert werden unter Verwendung von in der logarithmischen Leistung führt für eine kleine Anzahl von Artikel. Siehe http://java-performance.info/string-switch-performance/ – isnot2bad

Antwort

4

Yes Schalter mit Schnur ist ein syntatic Zucker. Von here

1) Strings in Switch sind syntaktischer Zucker, keine Änderung der JVM-Ebene.

2) Intern verwendet es equals-Methode zu vergleichen, das heißt, wenn Sie übergeben Null wird es werfen java.lang.NullPointerException, also Vorsicht vor dass.

3) Bei Strings in Switch-Anweisungen wird zwischen Groß- und Kleinschreibung unterschieden, vorzugsweise wird nur ein Fall verwendet und die Eingabe in den bevorzugten Fall konvertiert, bevor sie an switch statement übergeben wird.

Überprüfen Sie auch How String in Switch works in Java 7

Aus dem gleichen Link, wenn Sie das Beispiel sehen:

public class StringInSwitchCase{ 
    public static void main(String[] args) { 
     String mode = args[0]; switch (mode) { 
      case "ACTIVE": System.out.println("Application is running on Active mode"); 
       break; 
      case "PASSIVE": System.out.println("Application is running on Passive mode"); 
       break; 
      case "SAFE": System.out.println("Application is running on Safe mode"); 
     } } } 

und den dekompilierten Code:

import java.io.PrintStream; 
public class StringInSwitchCase{ 
    public StringInSwitchCase() { } 
    public static void main(string args[]) { 
     String mode = args[0]; String s; 
     switch ((s = mode).hashCode()) { 
      default: break; 
      case -74056953: if (s.equals("PASSIVE")) { 
       System.out.println("Application is running on Passive mode"); } 
       break; 
      case 2537357: if (s.equals("SAFE")) { System.out.println("Application is running on Safe mode"); } 
       break; 
      case 1925346054: if (s.equals("ACTIVE")) { System.out.println("Application is running on Active mode"); } 
       break; } } } 

werden Sie feststellen, dass String in Switch funktioniert mit hashCode() und equals() Methode.

Wie erwartet verwendet es hashCode() Methode zum Schalten und equals() Verfahren zur Verifikation bedeutet dies, es ist nur ein syntaktischer Zucker, anstatt eine eingebaute nativen Funktionalität.

+1

Die letzten beiden Aussagen dort treffen mich als widersprüchlich: ist der Unterschied in der Byte-Code-Effizienz wirklich so groß, wenn es erfordert, dass Sie eine neue, niedrigere/erstellen Großbuchstaben, um es zu benutzen? –

+4

Also ... wenn der Byte-Code effizienter ist, dann sind sie nicht genau syntaktischer Zucker. – RealSkeptic

+0

@JeroenVannevel: - Ja das ist widersprüchlich. Ich habe diesen Teil entfernt und den Code hinzugefügt, um ihn klarer zu machen. –

7

Der Compiler optimiert eine switch Anweisung basierend auf String-Werte der hashCode() Methode verwenden, und verwendet dann eine Lookup-Tabelle im Bytecode. Dies ist im Allgemeinen effizienter als eine if - else Anweisung.

Zum Beispiel die folgenden:

String string = "x"; 
switch(string) { 
    case "x": System.out.println("x"); 
       break; 
    case "y": System.out.println("y"); 
       break; 
    case "z": System.out.println("z"); 
       break; 
} 

ist diesem Bytecode umgewandelt:

ldc "x" 
astore_1 
aload_1 
astore_2 
iconst_m1 
istore_3 
aload_2 
invokevirtual java/lang/String/hashCode()I 
tableswitch 120 
    10 
    17 
    24 
    default: 30 
aload_2 
ldc "x" 
invokevirtual java/lang/String/equals(Ljava/lang/Object;)Z 
ifeq 30 
iconst_0 
istore_3 
goto 30 
aload_2 
ldc "y" 
invokevirtual java/lang/String/equals(Ljava/lang/Object;)Z 
ifeq 30 
iconst_1 
istore_3 
goto 30 
aload_2 
ldc "z" 
invokevirtual java/lang/String/equals(Ljava/lang/Object;)Z 
ifeq 30 
iconst_2 
istore_3 
iload_3 
tableswitch 0 
    32 
    36 
    40 
    default: 43 
getstatic java/lang/System/out Ljava/io/PrintStream; 
ldc "x" 
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V 
goto 43 
getstatic java/lang/System/out Ljava/io/PrintStream; 
ldc "y" 
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V 
goto 43 
getstatic java/lang/System/out Ljava/io/PrintStream; 
ldc "z" 
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V 
return 
+1

Warum wäre das effizienter? Ein Einschalten von im Wesentlichen zufälligen Schlüsseln kann keine Sprungtabelle verwenden. Die Sprungtabelle würde im schlimmsten Fall 2^32 Einträge benötigen. – usr

+1

@ usr Auch wenn eine Jump-Tabelle nicht verwendet werden kann, selbst wenn der Switch eine Reihe von if-else-Prüfungen für einen Integer-Wert beendet, ist dies immer noch weniger teuer als eine Reihe von if-else-Prüfungen für einen String-Wert. – hvd

Verwandte Themen