1046 votes

Pourquoi ne puis-je pas utiliser l'instruction switch sur une chaîne ?

Cette fonctionnalité sera-t-elle intégrée dans une version ultérieure de Java ?

Quelqu'un peut-il m'expliquer pourquoi je ne peux pas le faire, de la manière technique dont Java switch La déclaration fonctionne ?

203 votes

C'est dans la SE 7. 16 ans après sa demande. download.oracle.com/javase/tutorial/java/nutsandbolts/

88 votes

Sun a été honnête dans son évaluation : "Don't hold your breath." lol, bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179

3 votes

@raffian Je pense que c'est parce qu'elle a "soupiré" deux fois. Ils ont répondu un peu tard aussi, après presque 10 ans. Elle devait être en train d'emballer des boîtes à lunch pour ses petits-enfants à l'époque.

1025voto

erickson Points 127945

Les instructions de commutation avec String ont été mises en œuvre dans Java SE 7 d'au moins 16 ans après qu'ils aient été demandés pour la première fois. La raison de ce retard n'a pas été précisée, mais il est probable qu'il s'agisse de performances.

Mise en œuvre dans le JDK 7

Cette fonctionnalité a maintenant été mise en œuvre dans javac avec un processus de "dé-sucrage" ; une syntaxe propre et de haut niveau utilisant String constantes dans case est étendu au moment de la compilation en un code plus complexe suivant un modèle. Le code résultant utilise des instructions de la JVM qui ont toujours existé.

A switch con String est traduit en deux interrupteurs lors de la compilation. Le premier fait correspondre chaque chaîne à un entier unique - sa position dans le commutateur d'origine. Ceci est fait en commutant d'abord sur le code de hachage de l'étiquette. Le cas correspondant est un if qui teste l'égalité des chaînes de caractères ; s'il y a des collisions sur le hachage, le test est un test en cascade. if-else-if . Le deuxième changement reflète celui du code source original, mais remplace les étiquettes de casse par leurs positions correspondantes. Ce processus en deux étapes permet de préserver facilement le contrôle de flux du commutateur original.

Commutations dans la JVM

Pour plus de détails techniques sur switch vous pouvez vous référer à la spécification de la JVM, où l'option compilation des instructions de commutation est décrite. En bref, il existe deux instructions JVM différentes qui peuvent être utilisées pour un switch, en fonction de la sparsité des constantes utilisées par les cas. Les deux dépendent de l'utilisation de constantes entières pour chaque cas afin de s'exécuter efficacement.

Si les constantes sont denses, elles sont utilisées comme index (après avoir soustrait la valeur la plus faible) dans une table de pointeurs d'instructions - le tableswitch l'instruction.

Si les constantes sont éparses, une recherche binaire pour le cas correct est effectuée - le lookupswitch l'instruction.

En désensibilisant un switch sur String les deux instructions sont susceptibles d'être utilisées. Le site lookupswitch convient au premier allumage des codes de hachage pour retrouver la position originale du boîtier. L'ordinal qui en résulte convient naturellement pour un tableswitch .

Les deux instructions exigent que les constantes entières affectées à chaque cas soient triées au moment de la compilation. Au moment de l'exécution, alors que l'instruction O(1) performance de tableswitch apparaît généralement meilleur que le O(log(n)) performance de lookupswitch Si le tableau n'est pas suffisamment dense pour justifier le compromis espace-temps, une analyse est nécessaire pour déterminer si le tableau est suffisamment dense pour justifier le compromis espace-temps. Bill Venners a écrit un grand article qui couvre ce sujet de manière plus détaillée, ainsi qu'un aperçu des autres instructions de contrôle de flux Java.

Avant JDK 7

Avant le JDK 7, enum pourrait se rapprocher d'un String -Basé sur un commutateur. Celui-ci utilise les statiques valueOf générée par le compilateur sur chaque enum type. Par exemple :

Pill p = Pill.valueOf(str);
switch(p) {
  case RED:  pop();  break;
  case BLUE: push(); break;
}

27 votes

Il pourrait être plus rapide d'utiliser simplement If-Else-If au lieu d'un hachage pour un commutateur basé sur une chaîne de caractères. J'ai constaté que les dictionnaires sont assez coûteux lorsqu'ils ne stockent que quelques éléments.

88 votes

Un if-elseif-elseif-elseif-else pourrait être plus rapide, mais je prendrais le code le plus propre 99 fois sur 100. Les chaînes de caractères, étant immuables, mettent en cache leur code de hachage, de sorte que le "calcul" du hachage est rapide. Il faudrait profiler le code pour déterminer quel avantage il y a.

23 votes

La raison invoquée contre l'ajout de switch(String) est qu'il ne répondrait pas aux garanties de performance attendues des instructions switch(). Ils ne voulaient pas "induire en erreur" les développeurs. Franchement, je ne pense pas qu'ils devraient garantir les performances de switch() pour commencer.

129voto

JeeBee Points 11882

Si vous avez un endroit dans votre code où vous pouvez activer une chaîne, il peut être préférable de remanier la chaîne pour en faire une énumération des valeurs possibles, que vous pouvez activer. Bien entendu, vous limitez les valeurs potentielles des chaînes de caractères à celles de l'énumération, ce qui n'est pas forcément souhaitable.

Bien sûr, votre énumération pourrait avoir une entrée pour "autre", et une méthode fromString(String), alors vous pourriez avoir

ValueEnum enumval = ValueEnum.fromString(myString);
switch (enumval) {
   case MILK: lap(); break;
   case WATER: sip(); break;
   case BEER: quaff(); break;
   case OTHER: 
   default: dance(); break;
}

4 votes

Cette technique vous permet également de prendre des décisions sur des questions telles que l'insensibilité à la casse, les alias, etc. Au lieu de dépendre d'un concepteur de langage pour trouver la solution "taille unique".

3 votes

Je suis d'accord avec JeeBee, si vous utilisez des chaînes de caractères, vous avez probablement besoin d'un enum. La chaîne représente généralement quelque chose qui va vers une interface (utilisateur ou autre) qui peut ou non changer dans le futur, il vaut mieux la remplacer par des enums.

18 votes

Voir xefer.com/2006/12/switchonstring pour un bel exposé de cette méthode.

91voto

Ce qui suit est un exemple complet basé sur le post de JeeBee, utilisant les enum java au lieu d'utiliser une méthode personnalisée.

Notez qu'à partir de Java SE 7, vous pouvez utiliser un objet String dans l'expression de l'instruction switch.

public class Main {

    /**
    * @param args the command line arguments
    */
    public static void main(String[] args) {

      String current = args[0];
      Days currentDay = Days.valueOf(current.toUpperCase());

      switch (currentDay) {
          case MONDAY:
          case TUESDAY:
          case WEDNESDAY:
              System.out.println("boring");
              break;
          case THURSDAY:
              System.out.println("getting better");
          case FRIDAY:
          case SATURDAY:
          case SUNDAY:
              System.out.println("much better");
              break;

      }
  }

  public enum Days {

    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
  }
}

26voto

James Curran Points 55356

Les commutateurs basés sur des entiers peuvent être optimisés pour obtenir un code très efficace. Les commutations basées sur d'autres types de données ne peuvent être compilées que par une série d'instructions if().

C'est la raison pour laquelle le C et le C++ n'autorisent les commutations que pour les types entiers, car elles étaient inutiles pour les autres types.

Les concepteurs de C# ont décidé que le style était important, même s'il ne présentait aucun avantage.

Les concepteurs de Java ont apparemment pensé comme les concepteurs de C.

27 votes

Les commutations basées sur n'importe quel objet hachable peuvent être implémentées de manière très efficace en utilisant une table de hachage - voir .NET. Votre raison n'est donc pas tout à fait correcte.

0 votes

Oui, et c'est ce que je ne comprends pas. Ont-ils peur que le hachage des objets devienne, à long terme, trop cher ?

3 votes

@Nalandial : en fait, avec un petit effort de la part du compilateur, ce n'est pas cher du tout car lorsque l'ensemble des chaînes est connu, il est assez facile de générer un hachage parfait (ce n'est pas fait par .NET, cependant ; cela ne vaut probablement pas la peine non plus).

19voto

DJClayworth Points 11288

James Curran dit succinctement : "Les commutations basées sur des entiers peuvent être optimisées en un code très efficace. Les commutations basées sur d'autres types de données ne peuvent être compilées qu'en une série d'instructions if(). C'est pour cette raison que le C et le C++ n'autorisent les commutations que sur les types entiers, puisque cela était inutile avec les autres types."

Mon opinion, et ce n'est qu'une opinion, est que dès que vous commencez à utiliser des non primitives, vous devez commencer à penser à "equals" par rapport à "==". Premièrement, la comparaison de deux chaînes de caractères peut être une procédure assez longue, ce qui ajoute aux problèmes de performance mentionnés ci-dessus. Deuxièmement, s'il y a une commutation sur les chaînes de caractères, il y aura une demande pour la commutation sur les chaînes de caractères ignorant la casse, la commutation sur les chaînes de caractères considérant/ignorant la locale, la commutation sur les chaînes de caractères basées sur regex..... J'approuverais une décision qui fait gagner beaucoup de temps aux développeurs du langage au détriment d'une petite quantité de temps pour les programmeurs.

0 votes

Techniquement, les regexes "commutent" déjà, puisqu'elles sont essentiellement des machines à états ; elles n'ont simplement que deux "cas", matched y not matched . (Sans prendre en compte des choses telles que les groupes [nommés], etc.)

3 votes

docs.oracle.com/javase/7/docs/technotes/guides/language/ États : Le compilateur Java génère un bytecode généralement plus efficace à partir des instructions switch qui utilisent des objets String qu'à partir des instructions if-then-else enchaînées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X