Почему я не могу использовать оператор switch для String?

Эта функциональность будет добавлена ​​в более позднюю версию Java?

Может кто-нибудь объяснить, почему я не могу сделать это, например, в техническом смысле, как switchработает утверждение Java ?

3.12.2008 18:23:57
Это в SE 7. 16 лет после его запроса. download.oracle.com/javase/tutorial/java/nutsandbolts/…
angryITguy 21.07.2011 06:54:44
Sun была честна в своих оценках: "Don't hold your breath."lol, bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179
raffian 11.08.2013 19:49:00
@ raffian Я думаю, это потому, что она дважды вздохнула. Они тоже немного опоздали, после почти 10 лет. Возможно, она тогда упаковывала ланч-боксы своим внукам.
WeirdElfB0y 26.08.2016 11:17:57
15 ОТВЕТОВ
РЕШЕНИЕ

Операторы switch с Stringделами были реализованы в Java SE 7 , по крайней мере, через 16 лет после их первого запроса. Четкой причины задержки предоставлено не было, но, скорее всего, это было связано с производительностью.

Реализация в JDK 7

В настоящее время эта функция реализована в процессе javac «удаления сахара»; чистый высокоуровневый синтаксис с использованием Stringконстант в caseобъявлениях расширяется во время компиляции в более сложный код, следуя шаблону. Результирующий код использует инструкции JVM, которые существовали всегда.

А switchс Stringделами во время компиляции переводится в два ключа. Первая отображает каждую строку в уникальное целое число - ее положение в исходном переключателе. Это делается путем первого включения хеш-кода метки. Соответствующий случай - это ifоператор, который проверяет равенство строк; если в хэше есть коллизии, тест является каскадным if-else-if. Второй переключатель отражает это в исходном исходном коде, но заменяет метки регистра соответствующими позициями. Этот двухэтапный процесс позволяет легко сохранить управление потоком исходного переключателя.

Переключатели в JVM

Для большей технической глубины switchвы можете обратиться к Спецификации JVM, где описана компиляция операторов switch . В двух словах, есть две разные инструкции JVM, которые можно использовать для коммутатора, в зависимости от разреженности констант, используемых в случаях. Оба зависят от использования целочисленных констант для эффективного выполнения каждого случая.

Если константы плотные, они используются в качестве индекса (после вычитания наименьшего значения) в таблицу указателей инструкций - tableswitchинструкцию.

Если константы редкие, выполняется бинарный поиск правильного регистра - lookupswitchинструкция.

В де-Обсахаривания switchна Stringобъектах, обе инструкции могут быть использованы. lookupswitchПодходят для первого переключателя на хэш - кодах , чтобы найти исходное положение корпуса. Полученный порядковый номер является естественным соответствием для tableswitch.

Обе инструкции требуют, чтобы целочисленные константы, назначенные каждому случаю, были отсортированы во время компиляции. Во время выполнения, хотя O(1)производительность tableswitchобычно выглядит лучше, чем O(log(n))производительность lookupswitch, требуется некоторый анализ, чтобы определить, является ли таблица достаточно плотной, чтобы оправдать компромисс между пространством и временем. Билл Веннерс (Bill Venners) написал отличную статью, которая более подробно описывает это, а также подробный обзор других инструкций по управлению потоком Java.

До JDK 7

До JDK 7 enumможно было бы приблизиться к Stringпереключателю на основе. При этом используется статическийvalueOf метод, сгенерированный компилятором для каждого enumтипа. Например:

Pill p = Pill.valueOf(str);
switch(p) {
  case RED:  pop();  break;
  case BLUE: push(); break;
}
1003
13.07.2017 19:02:04
Возможно, быстрее будет просто использовать If-Else-If вместо хеша для строкового переключателя. Я нашел словари довольно дорогими, если хранить только несколько предметов.
Jonathan Allen 5.12.2008 07:17:14
If-elseif-elseif-elseif-else может быть быстрее, но я бы выбрал более чистый код 99 раз из 100. Строки, будучи неизменяемыми, кэшируют свой хэш-код, поэтому «вычисление» хеша происходит быстро. Нужно было бы профилировать код, чтобы определить, какая выгода есть.
erickson 5.12.2008 17:06:19
Причина, по которой против добавления switch (String) лежит то, что он не соответствует гарантиям производительности, ожидаемым от операторов switch (). Они не хотели «вводить в заблуждение» разработчиков. Честно говоря, я не думаю, что они должны гарантировать производительность switch () для начала.
Gili 22.12.2008 22:15:30
Если вы просто используете Pillдля выполнения каких-либо действий, основанных на, strя бы сказал, что если-еще предпочтительнее, так как он позволяет обрабатывать strзначения за пределами диапазона КРАСНЫЙ, СИНИЙ, без необходимости перехватывать исключение valueOfили вручную проверять совпадение с именем каждый тип перечисления, который просто добавляет ненужные издержки. По моему опыту имеет смысл использовать valueOfдля преобразования в перечисление, только если впоследствии было необходимо типизированное представление значения String.
MilesHampson 23.07.2013 10:07:02
Интересно, прилагают ли компиляторы какие-либо усилия, чтобы проверить, существует ли какая-либо пара чисел (x, y), для которой набор значений (hash >> x) & ((1<<y)-1)будет давать различные значения для каждой строки, hashCodeкоторая отличается, и (1<<y)меньше чем в два раза количество строк (или в хотя бы не намного больше, чем это).
supercat 18.12.2013 18:39:28

Переключатели, основанные на целых числах, могут быть оптимизированы для очень эффективного кода. Переключатели, основанные на другом типе данных, могут быть скомпилированы только в серию операторов if ().

По этой причине C & C ++ разрешает переключаться только на целочисленные типы, поскольку с другими типами это было бессмысленно.

Дизайнеры C # решили, что стиль важен, даже если не было никакого преимущества.

Дизайнеры Java, очевидно, думали как дизайнеры C.

26
3.12.2008 18:32:10
Переключатели, основанные на любом хешируемом объекте, могут быть реализованы очень эффективно с использованием хеш-таблицы - см. .NET. Так что твоя причина не совсем верна.
Konrad Rudolph 3.12.2008 18:36:38
Да, и это то, чего я не понимаю. Они боятся, что хеширование объектов в конечном итоге станет слишком дорогим?
Alex Beardsley 3.12.2008 18:41:32
@Nalandial: на самом деле, с небольшим усилием со стороны компилятора, это совсем не дорого, потому что, когда набор строк известен, довольно просто сгенерировать идеальный хеш (хотя это не делается в .NET; наверное, усилий тоже не стоит).
Konrad Rudolph 3.12.2008 20:53:11
@Nalandial & @Konrad Rudolph - хотя хэширование строки (из-за ее неизменности) кажется решением этой проблемы, вы должны помнить, что все неконечные объекты могут иметь свои функции хеширования переопределенными. Это затрудняет во время компиляции обеспечение согласованности в переключателе.
martinatime 3.12.2008 22:01:38
Вы также можете создать DFA для соответствия строке (как это делают механизмы регулярных выражений). Возможно, даже более эффективно, чем хеширование.
Nate C-K 22.08.2011 17:30:06

Если в вашем коде есть место, где вы можете включить строку, то может быть лучше реорганизовать строку, чтобы она представляла собой перечисление возможных значений, которые вы можете включить. Конечно, вы можете ограничить потенциальные значения строк, которые вы можете иметь, перечисленными в перечислении, что может быть или не быть желательным.

Конечно, в вашем перечислении может быть запись для «other» и метод fromString (String), тогда вы можете иметь

ValueEnum enumval = ValueEnum.fromString(myString);
switch (enumval) {
   case MILK: lap(); break;
   case WATER: sip(); break;
   case BEER: quaff(); break;
   case OTHER: 
   default: dance(); break;
}
125
3.12.2008 18:44:19
Этот метод также позволяет вам решать такие вопросы, как нечувствительность к регистру, псевдонимы и т. Д. Вместо того, чтобы полагаться на то, что разработчик языка придумает решение «один размер подходит всем».
Darron 3.12.2008 21:12:07
Согласитесь с JeeBee, если вы включаете строки, вероятно, нужен enum. Строка обычно представляет что-то, идущее в интерфейс (пользовательский или другой), которое может измениться или не измениться в будущем, поэтому лучше заменить его на
hhafez 3.12.2008 23:24:03
См. Xefer.com/2006/12/switchonstring для хорошего описания этого метода.
David Schmitt 14.04.2010 09:04:49
@DavidSchmitt У рецензии есть один существенный недостаток. Он перехватывает все исключения вместо тех, которые на самом деле выдает метод.
M. Mimpen 4.02.2014 12:48:31

Джеймс Керран кратко говорит: «Переключатели, основанные на целых числах, могут быть оптимизированы для очень эффективного кода. Переключатели, основанные на другом типе данных, могут быть скомпилированы только в серию операторов if (). По этой причине C & C ++ разрешает переключать только целочисленные типы, так как это было бессмысленно с другими типами ".

Мое мнение, и только оно заключается в том, что как только вы начинаете переключаться на не примитивы, вам нужно задуматься о «равных» и «==». Во-первых, сравнение двух строк может быть довольно длительной процедурой, добавляя к проблемам производительности, которые упомянуты выше. Во-вторых, если происходит переключение между строками, то будет требоваться включение строк без учета регистра, переключение строк с учетом / игнорирование локали, переключение строк на основе регулярных выражений .... Я бы одобрил решение, которое сэкономило много времени для Языковые разработчики за счет небольшого количества времени для программистов.

18
3.12.2008 20:49:42
Технически, регулярные выражения уже "переключаются", поскольку они в основном просто конечные автоматы; у них просто есть только два «дела», matchedи not matched. (Однако, не принимая во внимание такие вещи, как [именованные] группы / и т.д.).
JAB 30.07.2012 17:35:43
docs.oracle.com/javase/7/docs/technotes/guides/language/… состояния: компилятор Java обычно генерирует более эффективный байт-код из операторов switch, которые используют объекты String, чем из цепочечных операторов if-then-else.
Wim Deblauwe 14.07.2016 19:00:07

Помимо приведенных выше хороших аргументов, я добавлю, что многие люди сегодня считают switchустаревшим остатком процедурного прошлого Java (назад в C раз).

Я не полностью разделяю это мнение, я думаю, switchчто в некоторых случаях он может быть полезен, по крайней мере, из-за его скорости, и в любом случае он лучше, чем некоторые серии каскадных чисел, которые else ifя видел в некотором коде ...

Но на самом деле, стоит взглянуть на случай, когда вам нужен переключатель, и посмотреть, нельзя ли его заменить чем-то более оригинальным. Например, перечисления в Java 1.5+, возможно, HashTable или какая-то другая коллекция (иногда я сожалею, что у нас нет (анонимных) функций в качестве первоклассного гражданина, как в Lua - у которого нет переключателя - или JavaScript) или даже полиморфизма.

12
3.12.2008 21:45:38
«Иногда я сожалею, что у нас нет (анонимных) функций как гражданина первого класса» Это больше не так.
user8397947 18.01.2017 20:40:22
@dorukayhan Да, конечно. Но хотите ли вы добавить комментарий ко всем ответам за последние десять лет, чтобы сообщить миру о том, что мы можем получить их, если мы обновимся до более новых версий Java? :-D
PhiLho 24.01.2017 14:12:05

Ниже приведен полный пример, основанный на публикации JeeBee, с использованием перечислений java вместо использования пользовательских методов.

Обратите внимание, что в Java SE 7 и более поздних версиях вы можете использовать объект String в выражении оператора 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
  }
}
90
19.09.2014 19:09:35

Это легкий ветерок в Groovy; Я встраиваю groovy jar и создаю groovyслужебный класс, чтобы делать все это и многое другое, что я считаю раздражающим в Java (поскольку я застрял на Java 6 на предприятии).

it.'p'.each{
switch (it.@name.text()){
   case "choclate":
     myholder.myval=(it.text());
     break;
     }}...
-3
15.01.2015 01:40:54
@SSpoke Потому что это вопрос Java, а ответ на Groovy не по теме и бесполезный плагин.
Martin 11.04.2014 13:19:22
Даже в больших консервативных домах SW Groovy используется вместе с Java. JVM предоставляет среду, не зависящую от языка, больше, чем язык, чтобы смешивать и использовать наиболее подходящую парадигму программирования для решения. Так что, может быть, теперь я должен добавить фрагмент в Clojure, чтобы собрать больше голосов :) ...
Alex Punnen 22.05.2014 04:02:14
Кроме того, как работает синтаксис? Я предполагаю, что Groovy - это другой язык программирования ...? Сожалею. Я ничего не знаю о Groovy.
HyperNeutrino 26.01.2016 03:09:36

В течение многих лет мы использовали препроцессор (с открытым исходным кодом) для этого.

//#switch(target)
case "foo": code;
//#end

Предварительно обработанные файлы называются Foo.jpp и обрабатываются в Foo.java с помощью скрипта ant.

Преимущество заключается в том, что он перерабатывается в Java, работающую на версии 1.0 (хотя обычно мы поддерживали только версию 1.4). Кроме того, это было намного проще сделать (много строковых переключателей) по сравнению с вымышлением с помощью перечислений или других обходных путей - код было намного легче читать, поддерживать и понимать. IIRC (на данный момент не может предоставить статистику или технические обоснования) также был быстрее, чем естественные эквиваленты Java.

Недостатки в том, что вы не редактируете Java, так что это немного больше рабочего процесса (редактировать, обрабатывать, компилировать / тестировать), плюс IDE свяжет обратно с Java, которая немного запутана (переключатель становится серией логических шагов if / else) и порядок переключения не поддерживается.

Я бы не рекомендовал его для версии 1.7+, но это полезно, если вы хотите программировать Java, которая предназначена для более ранних версий JVM (поскольку в Joe public редко устанавливаются последние версии).

Вы можете получить его из SVN или просмотреть код онлайн . Вам понадобится EBuild, чтобы построить его как есть.

4
2.05.2015 14:38:26
Вам не нужна 1.7 JVM для запуска кода с переключателем String. Компилятор 1.7 превращает переключатель String в нечто, использующее ранее существующий байт-код.
Dawood ibn Kareem 4.12.2013 01:18:44

Пример прямого Stringиспользования начиная с версии 1.7 также может быть показан:

public static void main(String[] args) {

    switch (args[0]) {
        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;
    }

}
19
6.11.2015 14:32:55

Если вы не используете JDK7 или выше, вы можете использовать его hashCode()для имитации. Поскольку String.hashCode()обычно возвращает разные значения для разных строк и всегда возвращает одинаковые значения для одинаковых строк, это довольно надежно (разные строки могут создавать тот же хеш-код, что и @Lii, упомянутый в комментарии, например, "FB"и "Ea"). См. Документацию .

Итак, код будет выглядеть так:

String s = "<Your String>";

switch(s.hashCode()) {
case "Hello".hashCode(): break;
case "Goodbye".hashCode(): break;
}

Таким образом, вы технически включаете int.

В качестве альтернативы вы можете использовать следующий код:

public final class Switch<T> {
    private final HashMap<T, Runnable> cases = new HashMap<T, Runnable>(0);

    public void addCase(T object, Runnable action) {
        this.cases.put(object, action);
    }

    public void SWITCH(T object) {
        for (T t : this.cases.keySet()) {
            if (object.equals(t)) { // This means that the class works with any object!
                this.cases.get(t).run();
                break;
            }
        }
    }
}
8
26.01.2016 03:14:43
Две разные строки могут иметь один и тот же хеш-код, поэтому, если вы включите хеш-коды, может быть взята неверная ветвь регистра.
Lii 23.01.2016 20:45:25
@Lii Спасибо за указание на это! Хотя это маловероятно, но я бы не поверил, что это сработает. «FB» и «Ea» имеют одинаковый хэш-код, поэтому найти коллизию невозможно. Второй код, вероятно, более надежный.
HyperNeutrino 26.01.2016 03:13:00
Я удивлен, что это компилируется, поскольку, как caseя думал, операторы всегда должны быть постоянными значениями, и String.hashCode()это не так (даже если на практике вычисления никогда не менялись между JVM).
StaxMan 26.01.2018 03:28:15
@ StaxMan Хм интересно, я никогда не переставал наблюдать это. Но да, caseзначения операторов не должны быть определяемыми во время компиляции, поэтому они работают нормально.
HyperNeutrino 26.01.2018 03:47:31
public class StringSwitchCase { 

    public static void main(String args[]) {

        visitIsland("Santorini"); 
        visitIsland("Crete"); 
        visitIsland("Paros"); 

    } 

    public static void visitIsland(String island) {
         switch(island) {
          case "Corfu": 
               System.out.println("User wants to visit Corfu");
               break; 
          case "Crete": 
               System.out.println("User wants to visit Crete");
               break; 
          case "Santorini": 
               System.out.println("User wants to visit Santorini");
               break; 
          case "Mykonos": 
               System.out.println("User wants to visit Mykonos");
               break; 
         default: 
               System.out.println("Unknown Island");
               break; 
         } 
    } 

} 
-8
29.02.2016 20:27:23
ОП не спрашивает, как включить строку. Он / она спрашивает, почему он / она не может этого сделать из-за ограничений синтаксиса до JDK7.
HyperNeutrino 2.07.2015 13:29:19

Когда вы используете intellij также посмотрите на:

Файл -> Структура проекта -> Проект

Файл -> Структура проекта -> Модули

Если у вас есть несколько модулей, убедитесь, что вы установили правильный уровень языка на вкладке модуля.

-4
6.01.2016 12:13:26
Не уверен, насколько ваш ответ имеет отношение к вопросу. Он спросил, почему операторы строкового переключателя, подобные следующим, недоступны: String mystring = "кое-что"; switch (mystring) {case "кое-что" sysout ("попал сюда"); , , }
Deepak Agarwal 14.11.2016 22:43:01

В других ответах говорилось, что это было добавлено в Java 7 и даны обходные пути для более ранних версий. Этот ответ пытается ответить на вопрос «почему»

Java была реакцией на чрезмерные сложности C ++. Он был разработан, чтобы быть простым чистым языком.

String получил немного особой обработки языка, но мне кажется, что дизайнеры пытались свести к минимуму количество специальных оболочек и синтаксического сахара.

переключение строк довольно сложно под капотом, поскольку строки не являются простыми примитивными типами. Это не было обычной чертой в то время, когда Java разрабатывался и не очень хорошо сочетался с минималистским дизайном. Тем более, что они решили не использовать специальный регистр == для строк, было бы (и бывает) немного странно, когда case работает, а == нет.

Между 1.0 и 1.4 сам язык оставался почти таким же. Большинство улучшений Java были на стороне библиотеки.

Все изменилось с Java 5, язык был значительно расширен. Дальнейшие расширения следовали в версиях 7 и 8. Я ожидаю, что это изменение отношения было вызвано ростом C #

4
19.01.2017 06:09:48
Повествование о switch (String) соответствует истории, временной шкале, контексту cpp / cs.
Espresso 4.04.2018 15:48:29
Не реализовывать эту функцию было большой ошибкой, все остальное - дешевое оправдание. За эти годы Java потеряла многих пользователей из-за отсутствия прогресса и упрямства дизайнеров не развивать язык. К счастью, они полностью изменили направление и отношение после JDK7
firephil 9.03.2019 22:28:36

Не очень красиво, но вот еще один способ для Java 6 и ниже:

String runFct = 
        queryType.equals("eq") ? "method1":
        queryType.equals("L_L")? "method2":
        queryType.equals("L_R")? "method3":
        queryType.equals("L_LR")? "method4":
            "method5";
Method m = this.getClass().getMethod(runFct);
m.invoke(this);
-2
1.06.2017 06:33:28

JEP 354. Выражения-переключатели (предварительный просмотр) в JDK-13 и JEP-361. Выражения-переключатели (стандартный) в JDK-14 расширяют оператор switch, чтобы его можно было использовать в качестве выражения .

Теперь вы можете:

  • напрямую назначить переменную из выражения switch ,
  • используйте новую форму метки переключателя ( case L ->):

    Код справа от метки переключателя case L -> ограничен выражением, блоком или (для удобства) оператором throw.

  • использовать несколько констант для каждого случая, разделенных запятыми,
  • а также больше нет разрывов значений :

    Чтобы получить значение из выражения switch, breakоператор with value отбрасывается в пользу yieldоператора.

Так что демо из ответов ( 1 , 2 ) может выглядеть так:

  public static void main(String[] args) {
    switch (args[0]) {
      case "Monday", "Tuesday", "Wednesday" ->  System.out.println("boring");
      case "Thursday" -> System.out.println("getting better");
      case "Friday", "Saturday", "Sunday" -> System.out.println("much better");
    }
0
9.02.2020 10:39:17