Используете оператор switch с диапазоном значений в каждом случае?


94

Можно ли в Java написать оператор switch, в котором каждый case содержит более одного значения? Например (хотя очевидно, что следующий код работать не будет):

switch (num) {
    case 1 .. 5:
        System.out.println("testing case 1 to 5");
        break;
    case 6 .. 10:
        System.out.println("testing case 6 to 10");
        break;
}

Я думаю, это можно сделать в Objective C, есть ли подобное в Java? Или я должен просто использовать if, else ifзаявление вместо этого?


В простом C есть даже расширения, которые позволяют это.
arshajii

В таком случае, возможно if/else, это лучшее решение.
Эрик Ван,

Ответы:


100

В Java нет ничего подобного. Почему бы просто не сделать следующее?

public static boolean isBetween(int x, int lower, int upper) {
  return lower <= x && x <= upper;
}

if (isBetween(num, 1, 5)) {
  System.out.println("testing case 1 to 5");
} else if (isBetween(num, 6, 10)) {
  System.out.println("testing case 6 to 10");
}

1
Это прекрасно работает и очень просто. Также, если вы хотите выбрать числа, не входящие в диапазон, все, что вам нужно, это if (! IsBetween ..., хорошая работа.
a54studio

1
@missingfaktor Я немного раскритиковал ваш ответ. Итак, вы, вероятно, захотите что-нибудь ответить. +1 хотя.
Александр Малахов

@MCEmperor, пожалуйста, не редактируйте мой ответ в соответствии с вашими личными предпочтениями форматирования. Это бесполезное редактирование.
missingfaktor

@missingfaktor Извините. Но это изменение было одобрено давным-давно; Я даже не помнил, что когда-либо делал это.
MC Emperor

77

Самое близкое к такому поведению с помощью switchоператоров -

switch (num) {
case 1:
case 2:
case 3:
case 4:
case 5:
     System.out.println("1 through 5");
     break;
case 6:
case 7:
case 8:
case 9:
case 10:
     System.out.println("6 through 10");
     break;
}

Используйте ifзаявления.


30

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

switch ((int) num/10) {
    case 1:
        System.out.println("10-19");
        break;
    case 2:
        System.out.println("20-29");
        break;
    case 3:
        System.out.println("30-39");
        break;
    case 4:
        System.out.println("40-49");
        break;
    default:
        break;
}

Но, как видите, это можно использовать только тогда, когда диапазон фиксирован в каждом случае.


6
Это прекрасно работает с кодами состояния http, разделенными на 100.
Стефан

14

Я не думаю, что это можно сделать на Java. Лучше всего просто поместить код в последний регистр диапазона.

switch (num) {
  case 1: case 2: case 3: case 4: case 5: 
     System.Out.Println("testing case 1 to 5");
     break;
  case 6: case 7: case 8: case 9: case 10:
     System.Out.Println("testing case 6 to 10");
     break;
  default:
     //
}

11

Я знаю, что этот пост старый, но считаю, что этот ответ заслуживает признания. Нет необходимости избегать оператора switch. Это можно сделать в java, но с помощью оператора switch, а не case. Это предполагает использование тернарных операторов.

public class Solution {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int num = Integer.parseInt(sc.nextLine());

        switch ((1 <= num && num <= 5 ) ? 0 :
                (6 <= num && num <= 10) ? 1 : 2) {

            case 0:
                System.out.println("I'm between one and five inclusive.");
                break;
            case 1:
                System.out.println("I'm between 6 and 10 inclusive.");
                break;
            case 2:
                System.out.println("I'm not between one and five or 6 and 10 inclusive.");
                break;
        }
    }
}

1
Хорошее решение! С Java 7 и выше вы также можете включить String, чтобы ваши случаи были еще более самодокументированными, например, switch ((1 <= num && num <= 5 ) ? "1 .. 5" : ...а затем case "1 .. 5":.
Дэниел Виддис,

@DanielWiddis: Я бы предложил использовать перечисление вместо строк, чтобы обеспечить согласованность.
nmatt

@nmatt В целом согласен. Я пытался разрешить использование синтаксиса стиля «1 .. 5».
Дэниел Виддис,

2
@DanielWiddis: Безусловно, хорошая вещь; Я бы назвал константы перечисления примерно как FROM_1_TO_5. Обычно диапазоны имеют определенное значение, и в этом случае константы перечисления могут быть названы в соответствии со значением диапазона.
nmatt

1
Я думаю, мы можем согласиться, что любой вариант лучше, чем 0, 1 или 2.
Даниэль Виддис,

10
  case 1: case 2: case 3: case 4: case 5: 
         System.out.println("testing case 1 to 5");
         break;
  case 6: case 7: case 8: case 9: case 10:
         System.out.println("testing case 6 to 10");
         break;
  default:
         System.out.println("default"); 

6

Или вы можете использовать свои индивидуальные случаи по назначению и использовать случай по умолчанию, чтобы указать инструкции диапазона как:

switch(n) {
    case 1 : System.out.println("case 1"); break;
    case 4 : System.out.println("case 4"); break;
    case 99 : System.out.println("case 99"); break;
    default :
        if (n >= 10 && n <= 15)
            System.out.println("10-15 range"); 
        else if (n >= 100 && n <= 200)
            System.out.println("100-200 range");
        else
            System.out.println("Your default case");
        break;   
}

Я всегда так делаю, но также добавляю комментарий между случаями, например: // 10-15 см. Ниже по умолчанию
Perdi Estaquel

5

Попробуйте это, если вам нужно использовать переключатель.

public static int range(int num){ 
    if ( 10 < num && num < 20)
        return 1;
    if ( 20 <= num && num < 30)
        return 2;
    return 3;
}

public static final int TEN_TWENTY = 1;
public static final int TWENTY_THIRTY = 2;

public static void main(String[]args){
    int a = 110;
    switch (range(a)){
        case TEN_TWENTY: 
            System.out.println("10-20"); 
            break;
        case TWENTY_THIRTY: 
            System.out.println("20-30"); 
            break;
        default: break;
    }
}

не могли бы вы дать объяснение тоже ..?
user1140237 08

Функция range возвращает некоторое значение в соответствии с входным аргументом, поэтому здесь может быть выполнено определение «диапазона значений» вместе с предложением switch case.
zl9394 09

Это сработало для моего варианта использования, у меня есть 2 переменные типа int, и я хочу проверить, что 2 переменные находятся в одном диапазоне, например (10-20), (20-30), .... вот так
Самир Кази

Я бы предложил использовать enum вместо волшебных значений int.
nmatt

4

Нет, ты не можешь этого сделать. Лучшее, что ты можешь сделать, это

case 1:
case 2:
case 3:
case 4:
case 5: 
  System.Out.Println("testing case 1 to 5");
break;

4

Вот красивый и минималистичный способ

(num > 1 && num < 5) ? first_case_method() 
                     : System.out.println("testing case 1 to 5")
                     : (num > 5 && num < 7)  ? System.out.println("testing case 5 to 7") 
                     : (num > 7 && num < 8)  ? System.out.println("testing case 7 to 8") 
                     : (num > 8 && num < 9)  ? System.out.println("testing case 8 to 9") 
                     : ... 
                     : System.out.println("default");

Вы можете это объяснить ?? - Я действительно получаю «мини-если», но кроме этого idk.
Камин Паллаги

@Tunaki - это if..elseifальтернатива каскадному использованию тернарного оператора ( condition ? statementIfTrue : statementIfFalse).
Виллием

3

Можно сгруппировать несколько условий в одном caseоператоре, используя механизм пропуска, разрешенный операторами switch, он упоминается в учебнике по Java и полностью описан в разделе §14.11. Заявление о переключателе в спецификации языка Java .

Следующий фрагмент кода был взят из примера в руководстве, он вычисляет количество дней в каждом месяце (пронумерованных от месяца 1 до месяца 12):

switch (month) {
    case 1: case 3: case 5:
    case 7: case 8: case 10:
    case 12:
        numDays = 31;
        break;
    case 4: case 6:
    case 9: case 11:
        numDays = 30;
        break;
    case 2:
        if (((year % 4 == 0) && 
             !(year % 100 == 0))
             || (year % 400 == 0))
            numDays = 29;
        else
            numDays = 28;
        break;
    default:
        System.out.println("Invalid month.");
        break;
}

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

switch(num) {
    case 1: case 2: case 3: case 4: case 5:
        System.out.println("testing case 1 to 5");
        break;
    case 6: case 7: case 8: case 9: case 10:
        System.out.println("testing case 6 to 10");
        break;
}

3

Вы можете использовать enumдля представления своих диапазонов,

public static enum IntRange {
  ONE_TO_FIVE, SIX_TO_TEN;
  public boolean isInRange(int v) {
    switch (this) {
    case ONE_TO_FIVE:
      return (v >= 1 && v <= 5);
    case SIX_TO_TEN:
      return (v >= 6 && v <= 10);
    }
    return false;
  }

  public static IntRange getValue(int v) {
    if (v >= 1 && v <= 5) {
      return ONE_TO_FIVE;
    } else if (v >= 6 && v <= 10) {
      return SIX_TO_TEN;
    }
    return null;
  }
}

3

Он поддерживается начиная с Java 12. Ознакомьтесь с JEP 354 . Здесь нет возможности "диапазона", но тоже может быть полезно.

switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);//number of letters
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}

Вы также должны иметь возможность реализовать это в ints. Обратите внимание, что ваш оператор switch должен быть исчерпывающим (с использованием defaultключевого слова или с использованием всех возможных значений в операторах case).


2

Ответ @missingfaktor действительно правильный, но немного сложный. Код более подробный (по крайней мере, для непрерывных интервалов), чем мог бы быть, и требует перегрузок / приведений и / или параметризации для long, float, Integer и т. Д.

if (num < 1)
    System.Out.Println("invalid case: " + num); // you should verify that anyway
else if (num <= 5)
    System.Out.Println("1 to 5");
else if (num <= 10)
    System.Out.Println("6 to 10");
else if (num <= 42)
    System.Out.Println("11 to 42");
else    
    System.Out.Println("43 to +infinity");

3
Извините за поздний ответ, в последнее время я не могу следить за ответами SO. Я отвечу на вашу критику по очереди. 1. Код немного более подробный, да, и он также выполняет некоторые избыточные вычисления. Но эта ИМО способствует ясности в этом случае. В вашем коде нужно помнить предыдущие случаи, когда вы падаете по лестнице.
missingfaktor

2
Что касается требования перегрузок и приведения, это не обоснованная критика IMO. То, что в Java нет разумного способа абстрагироваться от числовых типов, говорит об ограничениях Java больше, чем об этом подходе. Вы можете изучить Numкласс типов Haskell, если хотите лучше понять этот момент.
missingfaktor

@missingfaktor 1. Думаю, это дело вкуса. Когда я имею дело с непрерывными интервалами, мне нравится думать о ifs так, как будто я постепенно избавляюсь от нижнего поддиапазона. Кроме того, избыточные вычисления не проблема вообще, но лишнее сравнение немного беспокоит: он становится проще сделать примыкающие сравнения из синхронизации, например , после некоторого редактирования: if (isBetween(x, 1, 4)) {...} else if (isBetween(x, 6, 10)). Во всяком случае, ничего страшного.
Александр Малахов

2. Согласовано - ограничение Java, но это то, с чем мы должны работать, и ваш исходный код будет дублироваться для разных типов. В этом конкретном случае проблему можно смягчить, используя класс Number , хотя подход Haskell лучше (никогда не приходил, чтобы по-настоящему изучить Haskell, но он напоминает «Concepts Light» C ++. Кроме того, я полагаю, вы имели в виду Real, что нет Num).
Александр Малахов

2

Используйте NavigableMapреализацию, например TreeMap.

/* Setup */
NavigableMap<Integer, Optional<String>> messages = new TreeMap<>();
messages.put(Integer.MIN_VALUE, Optional.empty());
messages.put(1, Optional.of("testing case 1 to 5"));
messages.put(6, Optional.of("testing case 6 to 10"));
messages.put(11, Optional.empty());

/* Use */
messages.floorEntry(3).getValue().ifPresent(System.out::println);

Отличное решение! не забудьте проверить значение null, if (messages.floorEntry(value)!=null) если вы используете целочисленное значение
Ch Vas

1
@ChVas То, как здесь настроена карта, floorEntry()никогда не вернется null(вставка Integer.MIN_VALUEключа предназначена для предотвращения этого). Но, независимо от типа вашего значения, вам нужно решить, как обрабатывать ключи за пределами допустимого диапазона.
erickson

0

Для номера входа в диапазоне 0..100

int n1 = 75; // input value
String res; int n=0; 
int[] a ={0,20,35,55,70,85,101};

for(; n1>=a[n]; n++);
switch(6-n+1) {
  case 1: res="A"; break;
  case 2: res="B"; break;
  case 3: res="C"; break;
  case 4: res="D"; break;
  case 5: res="E"; break;
  default:res="F";
}
System.out.println(res);

0

Этот тип поведения не поддерживается в Java. Однако, если у вас есть большой проект, которому это необходимо, подумайте о добавлении кода Groovy в свой проект. Код Groovy компилируется в байтовый код и может запускаться с JVM. Компания, в которой я работаю, использует Groovy для написания классов обслуживания и Java для написания всего остального.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.