Cette fonctionnalité va-t-elle être intégrée dans une version Java ultérieure?
Quelqu'un peut-il expliquer pourquoi je ne peux pas faire cela, comme dans, la façon technique dont fonctionne la switch
déclaration de Java ?
java
string
switch-statement
Alex Beardsley
la source
la source
"Don't hold your breath."
lol, bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179Réponses:
Les instructions de commutation avec des
String
cas ont été implémentées dans Java SE 7 , au moins 16 ans après leur première demande. Une raison claire de ce retard n'a pas été fournie, mais elle était probablement liée à la performance.Implémentation dans JDK 7
La fonction a maintenant été mise en œuvre
javac
avec un processus de "désucrage"; une syntaxe propre et de haut niveau utilisant desString
constantes dans lescase
déclarations est développée au moment de la compilation en un code plus complexe suivant un modèle. Le code résultant utilise des instructions JVM qui ont toujours existé.Un
switch
avecString
cas est traduit en deux commutateurs lors de la compilation. Le premier mappe chaque chaîne à un entier unique — sa position dans le commutateur d'origine. Cela se fait en activant d'abord le code de hachage de l'étiquette. Le cas correspondant est uneif
instruction qui teste l'égalité des chaînes; s'il y a des collisions sur le hachage, le test est en cascadeif-else-if
. Le deuxième commutateur reflète cela dans le code source d'origine, mais remplace les étiquettes de cas avec leurs positions correspondantes. Ce processus en deux étapes permet de conserver facilement le contrôle de flux du commutateur d'origine.Commutateurs dans la JVM
Pour plus de détails techniques
switch
, vous pouvez vous référer à la spécification JVM, où la compilation des instructions de commutateur est décrite. En résumé, deux instructions JVM différentes peuvent être utilisées pour un commutateur, selon la rareté des constantes utilisées par les cas. Les deux dépendent de l'utilisation de constantes entières pour chaque cas pour s'exécuter efficacement.Si les constantes sont denses, elles sont utilisées comme index (après soustraction de la valeur la plus basse) dans un tableau de pointeurs d'instructions - l'
tableswitch
instruction.Si les constantes sont rares, une recherche binaire du cas correct est effectuée - l'
lookupswitch
instruction.Lors du dépuchage
switch
d'String
objets sur , les deux instructions sont susceptibles d'être utilisées. Lelookupswitch
convient au premier interrupteur sur les codes de hachage pour trouver la position d'origine du boîtier. L'ordinal résultant est un ajustement naturel pour atableswitch
.Les deux instructions nécessitent que les constantes entières attribuées à chaque cas soient triées au moment de la compilation. Au moment de l'exécution, bien que les
O(1)
performances detableswitch
généralement semblent meilleures que lesO(log(n))
performances delookupswitch
, elles nécessitent une analyse pour déterminer si la table est suffisamment dense pour justifier le compromis espace-temps. Bill Venners a écrit un excellent article qui couvre cela plus en détail, ainsi qu'un regard sous le capot sur d'autres instructions de contrôle de flux Java.Avant JDK 7
Avant JDK 7,
enum
pouvait s'approcher d'unString
commutateur basé sur. Cela utilise lavalueOf
méthode statique générée par le compilateur sur chaqueenum
type. Par exemple:la source
Pill
une action basée surstr
Je dirais que si autre chose est préférable car cela vous permet de gérer desstr
valeurs en dehors de la plage ROUGE, BLEU sans avoir besoin d'attraper une exceptionvalueOf
ou de vérifier manuellement une correspondance avec le nom de chaque type d'énumération qui ajoute simplement une surcharge inutile. D'après mon expérience, cela n'a de sens que devalueOf
se transformer en énumération si une représentation sécurisée de type de la valeur String était nécessaire plus tard.(hash >> x) & ((1<<y)-1)
donnerait des valeurs distinctes pour chaque chaîne dont lahashCode
différence est, et(1<<y)
est inférieure au double du nombre de chaînes (ou à pas beaucoup plus que cela).Si vous avez un endroit dans votre code où vous pouvez activer une chaîne, il peut être préférable de refactoriser la chaîne pour qu'elle soit une énumération des valeurs possibles, que vous pouvez activer. Bien sûr, vous limitez les valeurs potentielles des chaînes que vous pouvez avoir à celles de l'énumération, qui peuvent ou non être souhaitées.
Bien sûr, votre énumération pourrait avoir une entrée pour «autre» et une méthode fromString (String), alors vous pourriez avoir
la source
Ce qui suit est un exemple complet basé sur la publication de JeeBee, utilisant les énumérations java au lieu d'utiliser une méthode personnalisée.
Notez que dans Java SE 7 et versions ultérieures, vous pouvez utiliser un objet String dans l'expression de l'instruction switch à la place.
la source
Les commutateurs basés sur des entiers peuvent être optimisés pour un code très efficace. Les commutateurs basés sur un autre type de données ne peuvent être compilés qu'en une série d'instructions if ().
Pour cette raison, C & C ++ n'autorise que les commutateurs sur les types entiers, car il était inutile avec d'autres types.
Les concepteurs de C # ont décidé que le style était important, même s'il n'y avait aucun avantage.
Les concepteurs de Java pensaient apparemment comme les concepteurs de C.
la source
Un exemple d'
String
utilisation directe depuis la version 1.7 peut également être montré:la source
James Curran dit succinctement: "Les commutateurs basés sur des entiers peuvent être optimisés pour un code très efficace. Les commutateurs basés sur d'autres types de données ne peuvent être compilés qu'en une série d'instructions if (). Pour cette raison, C & C ++ n'autorise que les commutateurs sur les types entiers, car il était inutile avec d'autres types. "
Mon avis, et ce n'est que cela, est que dès que vous commencez à activer des non-primitives, vous devez commencer à penser à "égal" par rapport à "==". Tout d'abord, la comparaison de deux chaînes peut être une procédure assez longue, s'ajoutant aux problèmes de performances mentionnés ci-dessus. Deuxièmement, s'il y a commutation de chaînes, il y aura une demande pour activer des chaînes en ignorant la casse, en activant les chaînes en tenant compte / en ignorant les paramètres régionaux, en activant les chaînes basées sur l'expression régulière ... J'approuverais une décision qui a fait gagner beaucoup de temps au les développeurs de langage au prix d'un peu de temps pour les programmeurs.
la source
matched
etnot matched
. (Sans prendre en compte des choses comme les groupes [nommés] / etc., Cependant.)En plus des bons arguments ci-dessus, j'ajouterai que beaucoup de gens considèrent aujourd'hui
switch
comme un reste obsolète du passé procédural de Java (retour à l'époque C).Je ne partage pas entièrement cette opinion, je pense que cela
switch
peut avoir son utilité dans certains cas, du moins à cause de sa vitesse, et de toute façon c'est mieux que certaines séries de cascading numériques queelse if
j'ai vues dans certains codes ...Mais en effet, il vaut la peine d'examiner le cas où vous avez besoin d'un interrupteur et de voir s'il ne peut pas être remplacé par quelque chose de plus OO. Par exemple, les énumérations dans Java 1.5+, peut-être HashTable ou une autre collection (parfois je regrette que nous n'ayons pas de fonctions (anonymes) en tant que citoyen de première classe, comme dans Lua - qui n'a pas de commutateur - ou JavaScript) ou même le polymorphisme.
la source
Si vous n'utilisez pas JDK7 ou supérieur, vous pouvez l'utiliser
hashCode()
pour le simuler. Étant donné queString.hashCode()
renvoie généralement des valeurs différentes pour différentes chaînes et renvoie toujours des valeurs égales pour des chaînes égales, il est assez fiable (différentes chaînes peuvent produire le même code de hachage que @Lii mentionné dans un commentaire, tel que"FB"
et"Ea"
) Voir la documentation .Ainsi, le code ressemblerait à ceci:
De cette façon, vous allumez techniquement un
int
.Vous pouvez également utiliser le code suivant:
la source
case
instructions devaient, je pensais, toujours être des valeurs constantes, et ceString.hashCode()
n'est pas le cas (même si dans la pratique le calcul n'a jamais changé entre les machines virtuelles Java).case
valeurs des instructions ne doivent pas être déterminables au moment de la compilation, donc cela fonctionne très bien.Nous utilisons depuis des années un préprocesseur (n open source).
Les fichiers prétraités sont nommés Foo.jpp et sont traités dans Foo.java avec un script ant.
L'avantage est qu'il est transformé en Java qui fonctionne sur 1.0 (bien que nous ne prenions généralement en charge que 1.4). De plus, il était beaucoup plus facile de le faire (beaucoup de commutateurs de chaîne) que de le truquer avec des énumérations ou d'autres solutions de contournement - le code était beaucoup plus facile à lire, à maintenir et à comprendre. IIRC (ne peut pas fournir de statistiques ou de raisonnement technique à ce stade), il était également plus rapide que les équivalents Java naturels.
Les inconvénients sont que vous n'éditez pas Java, c'est donc un peu plus de flux de travail (éditer, traiter, compiler / tester), plus un IDE sera lié à Java qui est un peu compliqué (le commutateur devient une série d'étapes logiques if / else) et l'ordre du boîtier de commutation n'est pas maintenu.
Je ne le recommanderais pas pour 1.7+, mais c'est utile si vous voulez programmer Java qui cible les JVM antérieures (puisque Joe public a rarement la dernière installée).
Vous pouvez l'obtenir auprès de SVN ou parcourir le code en ligne . Vous aurez besoin d' EBuild pour le construire tel quel.
la source
D'autres réponses ont indiqué que cela a été ajouté dans Java 7 et donné des solutions de contournement pour les versions antérieures. Cette réponse tente de répondre au "pourquoi"
Java était une réaction à la complexité excessive du C ++. Il a été conçu pour être un langage simple et propre.
String a obtenu un peu de traitement de cas spécial dans la langue, mais il me semble clair que les concepteurs essayaient de réduire au minimum la quantité de boîtier spécial et de sucre syntaxique.
l'activation des chaînes est assez complexe sous le capot car les chaînes ne sont pas de simples types primitifs. Ce n'était pas une caractéristique commune au moment où Java a été conçu et ne correspond pas vraiment au design minimaliste. D'autant plus qu'ils avaient décidé de ne pas mettre de cas spécial == pour les chaînes, il serait (et est) un peu étrange que la casse fonctionne où == ne fonctionne pas.
Entre 1.0 et 1.4, la langue elle-même est restée à peu près la même. La plupart des améliorations apportées à Java étaient du côté de la bibliothèque.
Tout cela a changé avec Java 5, le langage a été considérablement étendu. D'autres extensions ont suivi dans les versions 7 et 8. Je m'attends à ce que ce changement d'attitude soit provoqué par la montée de C #
la source
JEP 354: Switch Expressions (Preview) dans JDK-13 et JEP 361: Switch Expressions (Standard) dans JDK-14 étendra l' instruction switch afin qu'elle puisse être utilisée comme expression .
Maintenant vous pouvez:
case L ->
):La démonstration des réponses ( 1 , 2 ) pourrait donc ressembler à ceci:
la source
Pas très joli, mais voici une autre façon pour Java 6 et ci-dessous:
la source
C'est un jeu d'enfant à Groovy; J'inclus le pot groovy et crée une
groovy
classe utilitaire pour faire toutes ces choses et plus que je trouve exaspérant à faire en Java (car je suis bloqué en utilisant Java 6 dans l'entreprise.)la source
Lorsque vous utilisez intellij, regardez également:
Fichier -> Structure du projet -> Projet
Fichier -> Structure du projet -> Modules
Lorsque vous avez plusieurs modules, assurez-vous de définir le niveau de langue correct dans l'onglet module.
la source
la source