Когда использовать оператор switch в Java

Я ценю, что все, что может быть сделано с помощью состояния switch, может быть сделано с помощью оператора if else.

но есть ли стилистические правила для того, когда следует использовать переключатель, а не если еще statment.

16 ответов


Ну switch чувствует себя "легче", во многих случаях, чем if/else if лестницы, на мой взгляд. В принципе, у вас не так много синтаксиса с фигурными скобками и скобками на пути вашего кода. Это, как говорится, наследует Си. Это означает, что у вас есть break и только одна область для переменных, если вы не вводите новые блоки.

тем не менее, компилятор может оптимизировать switch операторы в таблицу поиска и выполняют проверку времени компиляции для литералов, когда работа с перечислениями. Итак, я бы предположил, что обычно предпочтительнее использовать switch над if/else if Если вы имеете дело с числовыми или перечислимых типов.


переключатель имеет одно преимущество, когда дело доходит до ясности:

switch (i) {
  case 1:
    // do something
    break;
  case 2:
  case 4:
    // do something
    break;
  case 5:
    // do something
    break;
}

Если код для 2 и 4 идентичны, это может быть более ясно, чем:

if ( i == 1 ) {
  // do something
}
if ( (i == 2) || (i == 4) ) {
  // do something
}
if ( (i == 5 ) {
  // do something
}

вам также проще (или другому программисту) разделить 2 и 4 случаях.


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


оператор switch используется при включении различных значений типов примитива / перечисления / оболочки. (И не все типы примитивов / оболочек, а только поддерживаемые-byte, short, char, int).

If / else обрабатывает остальное.

например, более эстетично сказать:

int i = getValueOfI();
switch (i) {
  case 1:
    // do something
    break;
  case 2:
    // do something
    break;

etc.

чем

if (i == 1) {

} else if (i == 2) {

}...

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


лично я использую, чтобы найти обе конструкции слишком процедурными. Хотя это может рассматриваться как экстермизм OO, я использую карту, содержащую экземпляры внутреннего интерфейса для каждого случая if. Я думаю, это позволяет лучше изолировать код. Однако, чтобы ответить на ваш вопрос, я использую switchs только тогда, когда у меня есть случаи, которые действительно перекрываются (я не использую операторы break). К сожалению, это действительно не ремонтопригодный блок кода.


Я бы предложил простое правило:

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

есть три веские причины. Один, в большинстве случаев switch быстрее, чем if/else каскад. Два, это делает цель яснее код. Три, о, так плохо забыто break дилемма-это меньшее зло, чем огромные if/else каскадом что случайно сломать потому что кто-то забыл else.

Java VMs фактически поддерживает два разных типа коммутатора: tableswitch и инструкцию lookupswitch. Tableswitch генерируется компилятором, если все case константы лежат в узком диапазоне, в противном случае он создает lookupswitch. Для больших switch операторы со многими случаями tableswitch более эффективен, чем lookupswitch. В lookupswitch, как правило, реализуется в определенной форме двоичного поиск.


есть два фактора для меня:

читаемость и хотите ли вы что-то решить, используя диапазоны значений или условий (в операторах switch вы можете использовать только одно целое число или перечисленное значение).


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

Это, как говорится, если это применимо, и есть по крайней мере 2 случая, то я использую переключатель. Его легче расширить, легче читать и проверять.


переключатель и перечислений.

Если значение перечисления, которое вы тестируете, может быть законным null по любой причине включение его в оператор switch создаст исключение NullPointerException. Не глядя на байтовый код, это немного озадачивает, что он это сделает.

объяснение: перечисления являются синтаксическим сахаром, введенным в 1.5. Оператор Switch по-прежнему работает с интами good-ole, но значения, которые он использует, являются ординалами, назначенными перечислению. Чтобы получить порядковый номер, перечисление значение должно быть ненулевым.

if statement, С другой стороны, был бы рад принять null для значения перечисления и просто провалить тест без NPE.


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


Я всегда находил, что оператор Java switch не так силен, как мне нужно. В его последний релиз lambdaj реализует его с умным использованием закрытия и Hamcrest matcher.

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

public List<String> sortStrings(List<String> list) {
    // a sort algorithm suitable for Strings
}

еще один, который хорошо работает с небольшими списками, имеющих не более 100 элементов:

public List<T> sortSmallList(List<T> list) {
    // a sort algorithm suitable for no more than 100 items
}

и более общая цель:

public List<String> sort(List<String> list) {
    // a generic sort algorithm
}

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

Switcher<List<T>> sortStrategy = new Switcher<List<T>>()
    .addCase(having(on(List.class).get(0), instanceOf(String.class))), 
        new Closure() {{ of(this).sortStrings(var(List.class)); }})
    .addCase(having(on(List.class).size(), lessThan(100))), 
        new Closure() {{ of(this).sortSmallList(var(List.class)); }})
    .setDefault(new Closure() {{ of(this).sort(var(List.class)); }});

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

List<T> sortedList = sortStrategy.exec(list, list);

ответ зависит от того, что именно вы делаете, а также распределение выбор.

Если одно условие является доминирующим, то if/then подходит.

if (i == 1){
  //do something
}else if (i == 2){
   // do something else
}

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

switch (i) {
  case 1:
     // do something
     break;
  case 2:
     // do something else
     break;
  ....
  case N: 
     // do yet something else
     break;
  }

что сказал, если производительность не важна идти с какой бы подход вы ни выбрали, вы предпочитаете как наиболее читаемый (поддерживаемый и простой в написании).

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

для" чрезвычайно больших " условий пример Марио переключателя lambdaj действительно классный и с осторожностью при инициализации приведет к очень высокой производительности. Это очень похоже на то, что генерирует оптимизатор. Я бы определил " чрезвычайно большой", как когда количество опций является большим или достаточно сложным, чтобы сделать его стоит ввести все, что в, и стоит путаницы поддержки, когда следовать за разработчиком пытается пройти через код. (Прокомментируйте свой код, почему у вас все это есть!).


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


как и в других языках, таких как C или c++, операторы switch полезны, когда вы хотите сравнить данную переменную со списком возможных значений и выполнить действие в зависимости от этих значений. Это более лаконичные, чем если бы еще утверждения.


я согласен с x4u по.

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

например, я только что наткнулся на этот код, который имеет switch на строку type и затем проверяет вторую строку extension с помощью if как case заявления.

 public abstract class Temp {

    boolean valid;

    public Temp() {
        String type = getType();
        String extension = getFilenameExtension();
        switch(type) {
            case "Image File": {
                if(!".jpg".equals(extension) && !".png".equals(extension)) {
                    warnWrongImageFormat();
                    valid = false;
                }
                break;
            }
            case "Zip File": {
                if(!".zip".equals(extension)) {
                    warnWrongZipFormat();
                    valid = false;
                }
                break;
            }
            default: {
                valid = true;
                break;
            }
        }
    }

    abstract String getType();
    abstract String getFilenameExtension();
    abstract void warnWrongImageFormat();
    abstract void warnWrongZipFormat();
}

вместо этого гораздо чище и менее сложно свести это к одному if else

public abstract class Temp {

    boolean valid;

    public Temp() {
        String type = getType();
        String extension = getFilenameExtension();
        valid = true;
        if("Image File".equals(type) && !".jpg".equals(extension) && !".png".equals(extension)) {
            warnWrongImageFormat();
            valid = false;
        }
        else if("Zip File".equals(type) && !".zip".equals(extension)) {
            warnWrongZipFormat();
            valid = false;
        }
    }

    abstract String getType();
    abstract String getFilenameExtension();
    abstract void warnWrongImageFormat();
    abstract void warnWrongZipFormat();
}

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

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

часто переключатель является признаком плохого дизайна OO, потому что вам лучше использовать полиморфизм.

единственным возможным преимуществом switch является то, что он более читаем. Но это

switch (i) {
  case 1:
    // do something
    break;
  case 2:
    // do something
    break;
}

более читаемый, чем это:

if (i == 1)
  //do something
else if (i == 2)
  // do something else

Я бы сказал: Нет! И у вас не будет недостатков switch.

мое предложение: попробуйте избежать переключения.