Wird diese Funktionalität in eine spätere Java-Version integriert?
Kann jemand erklären, warum ich das nicht tun kann, wie in der technischen Art und Weise, wie Javas switch
Aussage funktioniert?
java
string
switch-statement
Alex Beardsley
quelle
quelle
"Don't hold your breath."
lol, bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179Antworten:
Switch-Anweisungen mit
String
Fällen wurden in Java SE 7 mindestens 16 Jahre nach ihrer ersten Anforderung implementiert . Ein klarer Grund für die Verzögerung wurde nicht angegeben, hatte aber wahrscheinlich mit der Leistung zu tun.Implementierung in JDK 7
Die Funktion wurde jetzt
javac
mit einem "Entzuckern" -Verfahren implementiert. Eine saubere Syntax auf hoher Ebene unter Verwendung vonString
Konstanten incase
Deklarationen wird zur Kompilierungszeit nach einem Muster zu komplexerem Code erweitert. Der resultierende Code verwendet JVM-Anweisungen, die immer vorhanden waren.A
switch
mitString
Fällen wird während der Kompilierung in zwei Schalter übersetzt. Der erste ordnet jede Zeichenfolge einer eindeutigen Ganzzahl zu - ihrer Position im ursprünglichen Schalter. Dies erfolgt, indem zuerst der Hash-Code des Etiketts eingeschaltet wird. Der entsprechende Fall ist eineif
Anweisung, die die Zeichenfolgengleichheit testet. Wenn der Hash kollidiert, ist der Test eine Kaskadierungif-else-if
. Der zweite Schalter spiegelt dies im ursprünglichen Quellcode wider, ersetzt jedoch die Fallbezeichnungen durch die entsprechenden Positionen. Dieser zweistufige Prozess macht es einfach, die Flusskontrolle des ursprünglichen Schalters beizubehalten.Schalter in der JVM
Weitere technische Informationen
switch
finden Sie in der JVM-Spezifikation, in der die Zusammenstellung von switch-Anweisungen beschrieben wird. Kurz gesagt, es gibt zwei verschiedene JVM-Anweisungen, die für einen Switch verwendet werden können, abhängig von der Sparsamkeit der von den Fällen verwendeten Konstanten. Beide hängen von der Verwendung ganzzahliger Konstanten für jeden Fall ab, um eine effiziente Ausführung zu gewährleisten.Wenn die Konstanten dicht sind, werden sie als Index (nach Subtraktion des niedrigsten Werts) in eine Tabelle mit Befehlszeigern verwendet - den
tableswitch
Befehl.Wenn die Konstanten spärlich sind, wird eine binäre Suche nach dem richtigen Fall durchgeführt - der
lookupswitch
Anweisung.Beim Entzuckern
switch
vonString
Objekten werden wahrscheinlich beide Anweisungen verwendet. Daslookupswitch
ist für den ersten Einschalt-Hash-Code geeignet, um die ursprüngliche Position des Gehäuses zu ermitteln. Die resultierende Ordnungszahl ist eine natürliche Anpassung für atableswitch
.Beide Anweisungen erfordern, dass die jedem Fall zugewiesenen Ganzzahlkonstanten zur Kompilierungszeit sortiert werden. Während zur Laufzeit die
O(1)
Leistung von imtableswitch
Allgemeinen besser erscheint als dieO(log(n))
Leistung vonlookupswitch
, ist eine Analyse erforderlich, um festzustellen, ob die Tabelle dicht genug ist, um den Raum-Zeit-Kompromiss zu rechtfertigen. Bill Venners hat einen großartigen Artikel geschrieben , der dies ausführlicher behandelt, zusammen mit einem Blick unter die Haube auf andere Anweisungen zur Java-Flusskontrolle.Vor JDK 7
Vor JDK 7
enum
könnte sich einString
Schalter annähern . Dies verwendet die statischevalueOf
Methode, die vom Compiler für jedenenum
Typ generiert wird . Zum Beispiel:quelle
Pill
eine Aktion basierend auf verwenden,str
würde ich argumentieren, ob-else vorzuziehen ist, da Sie damitstr
Werte außerhalb des Bereichs ROT, BLAU verarbeiten können, ohne eine Ausnahme abfangenvalueOf
oder manuell nach einer Übereinstimmung mit dem Namen von suchen zu müssen Jeder Aufzählungstyp, der nur unnötigen Overhead hinzufügt. Nach meiner Erfahrung hat es nur Sinn gemacht,valueOf
eine Aufzählung zu verwenden, wenn später eine typsichere Darstellung des String-Werts benötigt wurde.(hash >> x) & ((1<<y)-1)
unterschiedliche Werte für jede ZeichenfolgehashCode
ergibt, die unterschiedlich ist und(1<<y)
weniger als die doppelte Anzahl von Zeichenfolgen (oder at) beträgt zumindest nicht viel größer als das).Wenn Sie in Ihrem Code eine Stelle haben, an der Sie einen String einschalten können, ist es möglicherweise besser, den String so umzugestalten, dass er eine Aufzählung der möglichen Werte enthält, die Sie einschalten können. Natürlich beschränken Sie die potenziellen Werte von Strings, die Sie haben können, auf diejenigen in der Aufzählung, die möglicherweise erwünscht sind oder nicht.
Natürlich könnte Ihre Aufzählung einen Eintrag für 'other' und eine fromString (String) -Methode haben, dann könnten Sie haben
quelle
Das Folgende ist ein vollständiges Beispiel, das auf dem Beitrag von JeeBee basiert und Java-Enums anstelle einer benutzerdefinierten Methode verwendet.
Beachten Sie, dass Sie in Java SE 7 und höher stattdessen ein String-Objekt im Ausdruck der switch-Anweisung verwenden können.
quelle
Auf Ganzzahlen basierende Schalter können auf sehr effizienten Code optimiert werden. Schalter, die auf einem anderen Datentyp basieren, können nur zu einer Reihe von if () -Anweisungen kompiliert werden.
Aus diesem Grund erlaubt C & C ++ nur das Umschalten auf ganzzahlige Typen, da dies bei anderen Typen sinnlos war.
Die Designer von C # entschieden, dass der Stil wichtig war, auch wenn es keinen Vorteil gab.
Die Designer von Java dachten anscheinend wie die Designer von C.
quelle
Ein Beispiel für die direkte
String
Verwendung seit 1.7 kann ebenfalls gezeigt werden:quelle
James Curran sagt kurz und bündig: "Schalter, die auf Ganzzahlen basieren, können auf sehr effizienten Code optimiert werden. Schalter, die auf einem anderen Datentyp basieren, können nur zu einer Reihe von if () -Anweisungen kompiliert werden. Aus diesem Grund erlauben C & C ++ nur Umschaltungen auf Ganzzahltypen. da war es bei anderen Typen sinnlos. "
Meine Meinung, und es ist nur die, ist, dass Sie, sobald Sie anfangen, Nicht-Primitive einzuschalten, anfangen müssen, über "gleich" gegen "==" nachzudenken. Erstens kann das Vergleichen von zwei Zeichenfolgen ein ziemlich langwieriger Vorgang sein, der zu den oben genannten Leistungsproblemen beiträgt. Zweitens, wenn es das Einschalten von Strings gibt, wird es erforderlich sein, Strings einzuschalten, Groß- und Kleinschreibung zu ignorieren, Strings einzuschalten, die das Gebietsschema berücksichtigen / ignorieren, Strings basierend auf Regex einzuschalten ... Ich würde eine Entscheidung gutheißen, die viel Zeit für die Sprachentwickler auf Kosten einer kleinen Zeitspanne für Programmierer.
quelle
matched
undnot matched
. (Ohne Berücksichtigung von Dingen wie [benannten] Gruppen / etc.)Neben den oben genannten guten Argumenten möchte ich hinzufügen, dass viele Leute heute
switch
einen veralteten Rest der prozeduralen Vergangenheit von Java sehen (zurück zur C-Zeit).Ich teile diese Meinung nicht vollständig, ich denke, sie
switch
kann in einigen Fällen nützlich sein, zumindest aufgrund ihrer Geschwindigkeit, und trotzdem ist sie besser als eine Reihe von kaskadierenden Zahlen, dieelse if
ich in einem Code gesehen habe ...In der Tat lohnt es sich jedoch, den Fall zu betrachten, in dem Sie einen Schalter benötigen, und zu prüfen, ob dieser nicht durch etwas OO ersetzt werden kann. Zum Beispiel Enums in Java 1.5+, vielleicht HashTable oder eine andere Sammlung (manchmal bedauere ich, dass wir keine (anonymen) Funktionen als erstklassiger Bürger haben, wie in Lua - das keinen Schalter hat - oder JavaScript) oder sogar Polymorphismus.
quelle
Wenn Sie JDK7 oder höher nicht verwenden, können Sie
hashCode()
es simulieren. DaString.hashCode()
normalerweise unterschiedliche Werte für unterschiedliche Zeichenfolgen und immer gleiche Werte für gleiche Zeichenfolgen zurückgegeben werden, ist dies ziemlich zuverlässig (unterschiedliche Zeichenfolgen können denselben Hashcode wie @Lii erzeugen, der in einem Kommentar erwähnt wird, z. B."FB"
und"Ea"
). Siehe Dokumentation .Der Code würde also so aussehen:
Auf diese Weise schalten Sie technisch ein
int
.Alternativ können Sie den folgenden Code verwenden:
quelle
case
Aussagen, wie ich dachte, immer konstante Werte sein mussten und diesString.hashCode()
nicht sind (auch wenn sich die Berechnung zwischen JVMs in der Praxis nie geändert hat).case
Anweisungswerte müssen zur Kompilierungszeit nicht bestimmbar sein, damit sie einwandfrei funktionieren.Seit Jahren verwenden wir dafür einen (n Open Source) Präprozessor.
Vorverarbeitete Dateien heißen Foo.jpp und werden mit einem Ant-Skript in Foo.java verarbeitet.
Vorteil ist, dass es in Java verarbeitet wird, das unter 1.0 ausgeführt wird (obwohl wir normalerweise nur bis 1.4 unterstützt haben). Außerdem war es viel einfacher, dies zu tun (viele String-Schalter), als es mit Aufzählungen oder anderen Problemumgehungen zu fummeln - Code war viel einfacher zu lesen, zu warten und zu verstehen. IIRC (kann derzeit keine Statistiken oder technischen Argumente liefern) war auch schneller als die natürlichen Java-Äquivalente.
Nachteile sind, dass Sie Java nicht bearbeiten, so dass es ein bisschen mehr Workflow ist (Bearbeiten, Verarbeiten, Kompilieren / Testen) und eine IDE eine Verbindung zu Java herstellt, die ein wenig kompliziert ist (der Schalter wird zu einer Reihe von if / else-Logikschritten). und die Schaltergehäusereihenfolge wird nicht beibehalten.
Ich würde es nicht für 1.7+ empfehlen, aber es ist nützlich, wenn Sie Java programmieren möchten, das auf frühere JVMs abzielt (da Joe public selten die neueste Version installiert hat).
Sie können es von SVN erhalten oder den Code online durchsuchen . Sie benötigen EBuild , um es so zu erstellen, wie es ist.
quelle
Andere Antworten besagten, dass dies in Java 7 hinzugefügt wurde und Problemumgehungen für frühere Versionen gegeben wurden. Diese Antwort versucht das "Warum" zu beantworten.
Java war eine Reaktion auf die Überkomplexität von C ++. Es wurde entworfen, um eine einfache saubere Sprache zu sein.
String hat ein wenig Sonderfallbehandlung in der Sprache, aber es scheint mir klar zu sein, dass die Designer versucht haben, die Menge an Spezialhülle und syntaktischem Zucker auf ein Minimum zu beschränken.
Das Einschalten von Strings ist unter der Haube ziemlich komplex, da Strings keine einfachen primitiven Typen sind. Es war zu der Zeit, als Java entworfen wurde, keine übliche Funktion und passt nicht wirklich gut zum minimalistischen Design. Zumal sie beschlossen hatten, keinen Sonderfall == für Zeichenfolgen zu verwenden, wäre (und ist) es ein bisschen seltsam, wenn der Fall dort funktioniert, wo == nicht funktioniert.
Zwischen 1.0 und 1.4 blieb die Sprache selbst ziemlich gleich. Die meisten Verbesserungen an Java waren auf der Bibliotheksseite.
Daß sich mit Java 5 alles geändert hat, wurde die Sprache erheblich erweitert. Weitere Erweiterungen folgten in den Versionen 7 und 8. Ich gehe davon aus, dass diese Änderung der Einstellung durch den Anstieg von C # verursacht wurde
quelle
JEP 354: Switch-Ausdrücke (Vorschau) in JDK-13 und JEP 361: Switch-Ausdrücke (Standard) in JDK-14 erweitern die switch-Anweisung, sodass sie als Ausdruck verwendet werden kann .
Jetzt kannst du:
case L ->
):Die Demo aus den Antworten ( 1 , 2 ) könnte also so aussehen:
quelle
Nicht sehr hübsch, aber hier ist ein anderer Weg für Java 6 und unten:
quelle
Es ist ein Kinderspiel in Groovy; Ich binde das groovige Glas ein und erstelle eine
groovy
Utility-Klasse, um all diese und weitere Dinge zu erledigen, die ich in Java als ärgerlich empfinde (da ich im Unternehmen nicht mehr mit Java 6 arbeiten kann).quelle
Wenn Sie Intellij verwenden, sehen Sie sich auch Folgendes an:
Datei -> Projektstruktur -> Projekt
Datei -> Projektstruktur -> Module
Wenn Sie mehrere Module haben, stellen Sie sicher, dass Sie auf der Registerkarte "Modul" das richtige Sprachniveau festlegen.
quelle
quelle