Warum kann Ihr Datentyp für switch-Anweisungen nicht lang sein, Java?

80

Hier ist ein Auszug aus den Java-Tutorials von Sun :

Ein Schalter arbeitet mit den byte, short, char, und intprimitiven Datentypen. Es funktioniert auch mit Aufzählungstypen (besprochen in Klassen und Vererbung) und ein paar speziellen Klassen , dass bestimmte primitive Typen „wrap“: Character, Byte, Short, und Integer(besprochen in Simple Data Objects).

Es muss einen guten Grund geben, warum der longprimitive Datentyp nicht zulässig ist. Weiß jemand was es ist?

Fostah
quelle

Antworten:

51

Ich denke, bis zu einem gewissen Grad war es wahrscheinlich eine willkürliche Entscheidung, die auf der typischen Verwendung von Schaltern beruhte.

Ein Schalter kann im Wesentlichen auf zwei Arten implementiert werden (oder im Prinzip als Kombination): Für eine kleine Anzahl von Fällen oder solche, deren Werte weit verteilt sind, wird ein Schalter im Wesentlichen das Äquivalent einer Reihe von ifs auf einer temporären Variablen (der Der eingeschaltete Wert darf nur einmal ausgewertet werden. Für eine moderate Anzahl von Fällen, deren Wert mehr oder weniger aufeinanderfolgend ist, wird eine Switch-Tabelle verwendet (die Anweisung TABLESWITCH in Java), wobei der Ort, zu dem gesprungen werden soll, effektiv in einer Tabelle nachgeschlagen wird.

Jede dieser Methoden könnte im Prinzip einen langen Wert anstelle einer ganzen Zahl verwenden. Aber ich denke, es war wahrscheinlich nur eine praktische Entscheidung, die Komplexität des Befehlssatzes und des Compilers mit dem tatsächlichen Bedarf in Einklang zu bringen: Die Fälle, in denen Sie wirklich lange umschalten müssen, sind selten genug, dass es akzeptabel ist, als neu schreiben zu müssen Reihe von IF-Anweisungen oder auf andere Weise umgehen (wenn die fraglichen langen Werte nahe beieinander liegen, können Sie in Ihrem Java-Code das int-Ergebnis des Subtrahierens des niedrigsten Werts umschalten).

Neil Coffey
quelle
Ich muss dem Argument "Seltenheit" zustimmen, da ich mich schon eine Weile in Java entwickelt habe und nie auf eine Situation gestoßen bin, in der ich bis jetzt lange einschalten musste / wollte.
Fostah
3
Dimitris, meine Argumentation basierte nicht auf einem Mittelverständnis der Thread-Sicherheit, nur dass ich nicht denke, dass das von Ihnen vorgebrachte Argument der Thread-Sicherheit zutrifft.
Neil Coffey
13
Die dümmste Designentscheidung aller Zeiten. Ich habe eine lange, die ein paar Flaggen ist, und ich muss mit tragen, wenn ... sonst wenn ... sonst ... total lächerlich.
m0skit0
2
Ich stimme w / @ m0skit0 zu. In meinem Fall hat jemand anderes eine API geschrieben, die Longs als Konstanten verwendet, da diese in der Datenbank gespeichert sind. Jetzt kann ich wegen der schlechten Entscheidung eines anderen keinen Schalter / Koffer verwenden.
CodeChimp
1
Ich sage nicht, dass es "Ende der Welt" ist. Und ja, es gibt Möglichkeiten, aber sie scheinen alle wie Hacks zu sein. Mein Punkt ist, dass wir als Entwickler immer versuchen sollten, darüber nachzudenken, wie wir glauben, dass die Leute das Zeug, das wir bauen, verwenden werden. In diesem Fall traf jemand die Entscheidung, Switch / Case nicht lange zuzulassen, basierend auf dem anfänglichen Gedanken, dass "Wer würde ehrlich 2 ^ 64 Fälle haben". Vielleicht ist das eine Vereinfachung. Vielleicht gibt es einen anderen Grund, warum Longs nicht gewechselt werden können, dass wir einfach auch nicht eingeweiht waren. Für mich ist es nur seltsam, es nicht zu unterstützen.
CodeChimp
21

Weil sie nicht die notwendigen Anweisungen im Bytecode implementiert haben und Sie wirklich nicht so viele Fälle schreiben möchten, egal wie "produktionsbereit" Ihr Code ist ...

[EDIT: Auszug aus Kommentaren zu dieser Antwort, mit einigen Ergänzungen im Hintergrund]

Um genau zu sein, 2³² sind viele Fälle und jedes Programm mit einer Methode, die lang genug ist, um mehr als das zu halten, wird absolut schrecklich sein! In jeder Sprache. (Die längste Funktion, die ich in einem Code in einer Sprache kenne, ist etwas mehr als 6 KB SLOC - ja, es ist eine große switch- und sie ist wirklich nicht zu handhaben.) Wenn Sie wirklich nicht wissen , longwo Sie nur eine intoder weniger haben sollten, dann hast du zwei echte Alternativen.

  1. Verwenden Sie eine Variante zum Thema Hash-Funktionen, um die longin eine zu komprimieren int. Die einfachste Möglichkeit, nur wenn Sie den falschen Typ haben, ist das Casting! Nützlicher wäre dies:

    (int) ((x&0xFFFFFFFF) ^ ((x >>> 32) & 0xFFFFFFFF))
    

    vor dem Einschalten des Ergebnisses. Sie müssen herausfinden, wie Sie die Fälle, gegen die Sie testen, auch transformieren können. Aber wirklich, das ist immer noch schrecklich, da es nicht das eigentliche Problem vieler Fälle anspricht.

  2. Eine viel bessere Lösung, wenn Sie mit einer sehr großen Anzahl von Fällen arbeiten, besteht darin, Ihr Design auf die Verwendung eines Map<Long,Runnable>oder eines ähnlichen zu ändern , damit Sie nachschlagen können, wie ein bestimmter Wert versendet wird. Auf diese Weise können Sie die Fälle in mehrere Dateien aufteilen. Dies ist viel einfacher zu verwalten, wenn die Anzahl der Fälle groß wird. Die Organisation der Registrierung des Hosts der beteiligten Implementierungsklassen wird jedoch komplexer (Anmerkungen können hilfreich sein, indem Sie dies zulassen) Registrierungscode automatisch erstellen).

    FWIW, ich habe dies vor vielen Jahren getan (wir haben im Verlauf des Projekts auf das neu veröffentlichte J2SE 1.2 umgestellt), als ich eine benutzerdefinierte Bytecode-Engine zur Simulation massiv paralleler Hardware erstellt habe (nein, die Wiederverwendung der JVM wäre aufgrund der radikalen Situation nicht geeignet gewesen verschiedene Wert- und Ausführungsmodelle beteiligt) und es hat den Code im Vergleich zu dem großen switch, den die C-Version des Codes verwendete, enorm vereinfacht .

Um die Take-Home - Nachricht wiederholen, zu wollen , switchauf eine longist ein Hinweis darauf , dass Sie entweder die Typen in Ihrem Programm falsch haben oder dass Sie bauen ein System mit so viel Variation beteiligt , dass Sie sollten mithilfe von Klassen werden. In jedem Fall Zeit zum Umdenken.

Donal Fellows
quelle
1
Aber ist der umgeschaltete Bereich wirklich mehr als 32 Bit breit? Ich habe noch nie von Code gehört, der so viele Schalterarme benötigt. Ohne dies können Sie den Bereich komprimieren (z. B. indem Sie eine Variation des Themas der Hash-Funktionen verwenden), um etwas zu erstellen, das funktioniert. Oder verwenden Sie a Map<Long,Runnable>, um das Problem auf eine ganz andere Weise zu lösen. :-)
Donal Fellows
3
@ Lord Torgamus: Vermutlich, weil es albern ist. Denken Sie einen Moment darüber nach: Warum sollte jemand, irgendjemand , Code mit mehr als 2 32 Armen in einem haben switch? Wenn Sie sich für eine endliche Menge so vieler Elemente entscheiden möchten, deutet dies einfach auf einen Fehler in der grundlegenden Gestaltung des Programms hin. Dass die Leute danach fragen, zeigt lediglich an, dass ** sie ihr Design falsch verstanden haben .
Donal Fellows
1
Übrigens, wenn jemand weiter mit mir darüber streiten möchte, geben Sie bitte zunächst einen Anwendungsfall für das Einschalten eines langen. Andernfalls werden wir für immer mit Hypothesen streiten ...
Donal Fellows
2
@DonalFellows, Ein weiterer Fall ist in Android mit ListView. Während listViews eine longAnzahl von Zeilen haben können, benötige ich speziell nur 4. Dies ist ein Fall, in dem mir eine longHand übergeben wird, die angibt, auf welche Zeile reagiert wird, und ich muss je nach Zeile verschiedene Dinge tun. Ich denke, ich könnte auf int umwandeln, aber es hätte mir das Leben leichter gemacht, wenn ich nur switchdie Variable bearbeitet hätte. So wie es ist, verwende ich stattdessen nur eine Zeichenfolge von ifund else if.
Christian Smith
7
"Um die Nachricht zum Mitnehmen zu wiederholen: Wenn Sie lange einschalten möchten, bedeutet dies, dass Sie entweder die falschen Typen in Ihrem Programm haben." - Nein, ist dies nicht! Ein zu haben longbedeutet nicht, dass Sie alle Möglichkeiten prüfen werden, genau wie ein intoder ein zu haben, Stringbedeutet das auch nicht. Dies bedeutet, dass Ihre Werte, die eine Handvoll sein können, einen großen Bereich haben . Sie könnten ein paar einfache Fälle überprüfen und defaultfür den Rest hineinfallen . Wenn Sie Schichten und Besetzungen durchführen müssen, besteht die Gefahr, dass Sie Daten verlieren. Unterm Strich ist es eine schlechte Java-Designentscheidung, kein Benutzerproblem.
jbx
6

Weil der Nachschlagetabellenindex 32 Bit betragen muss.

JRL
quelle
3
Andererseits switchmuss nicht unbedingt eine Nachschlagetabelle implementiert werden.
Joachim Sauer
9
Wenn dies der Fall wäre, könnten sie niemals einen Schalter für Strings implementieren (wie sie derzeit planen).
Dimitris Andreou
1
@ DimitrisAndreou ja, wir können jetzt in Strings: D (seit Jahren: P)
J. Doe
-11

Eine lange 32-Bit-Architektur wird durch zwei Wörter dargestellt . Stellen Sie sich nun vor, was passieren könnte, wenn die Ausführung der switch-Anweisung aufgrund unzureichender Synchronisation mit ihren hohen 32 Bits von einem Schreibvorgang und den 32 niedrigen von einem anderen einen langen Wert beobachtet! Es könnte versuchen zu gehen ... wer weiß wo! Grundsätzlich irgendwo zufällig. Selbst wenn beide Schreibvorgänge gültige Fälle für die switch-Anweisung darstellen würden, würde ihre lustige Kombination wahrscheinlich weder zum ersten noch zum zweiten führen - oder extrem schlimmer, es könnte zu einem anderen gültigen, aber nicht verwandten Fall führen!

Zumindest mit einem int (oder kleineren Typen), egal wie stark Sie es vermasseln, liest die switch-Anweisung zumindest einen Wert, den jemand tatsächlich geschrieben hat , anstelle eines Werts "aus dem Nichts".

Natürlich kenne ich den tatsächlichen Grund nicht (es ist mehr als 15 Jahre her, ich habe nicht so lange aufgepasst!), Aber wenn Sie erkennen, wie unsicher und unvorhersehbar ein solches Konstrukt sein könnte, werden Sie dem zustimmen Dies ist definitiv ein sehr guter Grund , niemals Longs einzuschalten (und wie es beabsichtigt ist, wird es 32-Bit-Maschinen geben, dieser Grund bleibt gültig).

Dimitris Andreou
quelle
1
Ich denke nicht, dass dies folgt. Der eingeschaltete Wert muss berechnet und in einem Register oder auf dem Stapel gespeichert werden. Wenn dieser Wert basierend auf Daten berechnet wird, auf die mehrere Threads zugreifen, muss diese Berechnung unabhängig von der Breite des Ergebnisses threadsicher gemacht werden. Sobald sich dieses Ergebnis jedoch in einem Register oder auf dem Stapel befindet, wird es nur vom Switching-Thread aufgerufen und ist sicher.
Neil Coffey
Neil, dein Argument ist ziemlich verwirrt: "Aber sobald dieses Ergebnis in einem Register oder auf dem Stapel ist, wird es nur vom Switching-Thread aufgerufen und ist sicher." Sicher, die Verwendung dieses Wertes ist threadsicher! Mein Punkt ist jedoch, dass dieser Wert aufgrund von Synchronisationsfehlern im Benutzercode bereits falsch sein kann . Thread-sicher zu verwenden ist ein falscher Wert weniger als nützlich :) Dieses Problem kann niemals behoben werden: Fehlerhafter gleichzeitiger Code könnte bereits den "aus der Luft" / falschen langen Wert erzeugt haben, der anschließend im Switch verwendet werden kann, wodurch der Wechseln Sie zu einer Falladresse, die noch niemand angegeben hat .
Dimitris Andreou
1
Dimitris, vielleicht ist etwas in deiner Argumentation, das ich nicht verstehe. Der eingeschaltete Wert kann aufgrund von Synchronisationsfehlern im Benutzercode tatsächlich falsch sein. Ich glaube jedoch nicht, dass die switch-Anweisung etwas Eigenes enthält, das dies wahrscheinlicher macht als in anderen Fällen. Und wenn ich es mir so gut wie möglich überlege, glaube ich nicht, dass die Nichtatomarität von Hi / Low-Wörtern mit langen Lese- / Schreibvorgängen im Gedächtnis tatsächlich ein Problem ist. (Überlegen Sie sich die Dinge anders: Sie könnten entscheiden, dass ein Wenn-Vergleich auf einer langen Basis aufgrund desselben Arguments nicht zulässig ist.)
Neil Coffey
Während die potenziellen Probleme, lange Zeit als zwei Wörter ohne garantierte atomare Schreibvorgänge dargestellt zu werden, ein allgemeines Problem sind, wäre man sich im Falle eines Wechsels einer noch größeren Gefahr gegenüber. Es ist, als würde man einen Umschlag mit einer Nachricht senden, bei der die Hälfte der Adresse von einer Person stammt, die andere Hälfte von einer anderen - die endgültige Adresse könnte gültig sein und einem völlig zufälligen Kerl entsprechen, der dann den Umschlag erhalten und entsprechend handeln würde. Es ist eine Sache, Müll zu lesen und Müll zu produzieren (wie ein falscher Boolescher Wert), aber Müll zu lesen und zufällige Sprünge zu machen, klingt für mich ein bisschen gefährlicher.
Dimitris Andreou
7
Ich weiß, dass dies alt ist und das Kommentieren ist ein bisschen umstritten, aber ich möchte unterstreichen, dass Ihr Argument auch gilt ifund das Ergebnis genauso schlecht wäre: falsches Ergebnis ~> falscher Zweig genommen. Erstellen einer lang if- else- ifKette anstelle eines switchführen würde tatsächlich zu genau dem gleichen Ergebnis.
Nicolai Parlog