Usando a instrução switch com um intervalo de valor em cada caso?


91

Em Java, é possível escrever uma instrução switch onde cada caso contém mais de um valor? Por exemplo (embora claramente o código a seguir não funcione):

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;
}

Acho que isso pode ser feito em Objective C, há algo semelhante em Java? Ou devo apenas usar declarações if, em else ifvez disso?


Existem até extensões em C simples que permitem isso.
arshajii

Respostas:


97

Java não tem nada desse tipo. Por que não apenas fazer o seguinte?

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
Isso funciona muito bem e é simples. Além disso, se você quiser selecionar números fora do intervalo, tudo o que você precisa é se (! IsBetween ..., bom trabalho.
a54studio

1
@missingfaktor tenho criticado a sua resposta um pouco. Então, você provavelmente gostaria de responder algo. 1 embora.
Alexander Malakhov

@MCEmperor, por favor, não edite minha resposta com suas preferências pessoais de formatação. Essa não é uma edição útil.
missingfaktor

@missingfaktor Desculpe. Mas essa edição foi aprovada há muito tempo; Eu nem me lembrava de ter feito isso.
MC Imperador

76

O mais próximo que você pode chegar desse tipo de comportamento com switchdeclarações é

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;
}

Use ifdeclarações.


29

outra alternativa é usar a operação matemática dividindo-a, por exemplo:

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;
}

Mas, como você pode ver, isso só pode ser usado quando o intervalo é fixo em cada caso.


6
Isso funciona bem com códigos de status http divididos por 100.
Stefan

13

Eu não acho que você pode fazer isso em Java. A melhor aposta é apenas colocar o código no último caso do intervalo.

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:
     //
}

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"); 

9

Sei que este post é antigo, mas acredito que essa resposta merece algum reconhecimento. Não há necessidade de evitar a instrução switch. Isso pode ser feito em java, mas por meio da instrução switch, não nos casos. Envolve o uso de operadores ternários.

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
Ótima solução! Com o Java 7 e superior, você também pode ativar uma String para que seus casos sejam ainda mais autodocumentados, por exemplo, switch ((1 <= num && num <= 5 ) ? "1 .. 5" : ...e depois case "1 .. 5":.
Daniel Widdis

@DanielWiddis: Eu sugiro usar um enum em vez de strings, para garantir a consistência.
nmatt

@nmatt Geralmente concordo. Eu estava tentando permitir o uso da sintaxe de estilo "1 .. 5".
Daniel Widdis

2
@DanielWiddis: Certamente uma coisa boa; Eu nomearia as constantes enum como FROM_1_TO_5. Normalmente, os intervalos têm um determinado significado e, nesse caso, as constantes enum podem ser nomeadas de acordo com o significado do intervalo.
nmatt

1
Acho que podemos concordar que qualquer uma das opções é melhor do que 0, 1 ou 2.
Daniel Widdis

6

Ou você pode usar seus casos solo como pretendido e usar seu caso padrão para especificar instruções de intervalo como:

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;   
}

Isso é o que eu sempre faço, mas também adiciono um comentário entre os casos, por exemplo: // 10 - 15 veja o padrão abaixo
Perdi Estaquel

5

Tente fazer isso se precisar usar o switch.

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;
    }
}

você pode dar uma explicação também ..?
user1140237

A função range retorna algum valor de acordo com o argumento de entrada, então a detecção de "range of value" pode ser feita aqui, juntamente com a cláusula switch case.
zl9394

Isso funcionou para o meu caso de uso, eu tenho 2 variáveis ​​inteiras e quero verificar se 2 variáveis ​​estão no mesmo intervalo, como (10-20), (20-30), .... assim
Sameer Kazi

Eu sugeriria usar um enum em vez de valores int mágicos.
nmatt

4

Não, você não pode fazer isso. O melhor que você pode fazer é isso

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

3

É possível agrupar várias condições na mesma caseinstrução usando o mecanismo de queda permitido pelas instruções switch, é mencionado no tutorial Java e totalmente especificado na seção §14.11. A declaração switch da especificação da linguagem Java .

O seguinte snippet de código foi retirado de um exemplo no tutorial, ele calcula o número de dias em cada mês (numerado do mês 1 ao mês 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;
}

Como você pode ver, para cobrir uma faixa de valores em uma única caseinstrução, a única alternativa é listar cada um dos valores possíveis individualmente, um após o outro. Como um exemplo adicional, veja como implementar o pseudocódigo na pergunta:

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

Você pode usar um enumpara representar seus intervalos,

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

Aqui está uma maneira linda e minimalista de ir

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

Você pode explicar isso ?? - eu entendo os 'mini ifs', mas diferente daquele idk.
Kamin Pallaghy

@Tunaki esta é uma if..elseifalternativa usando em cascata o operador ternário ( condition ? statementIfTrue : statementIfFalse).
Williem

3

É compatível com Java 12. Confira JEP 354 . Sem possibilidades de "alcance" aqui, mas também pode ser útil.

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);
}

Você deve ser capaz de implementar isso também no Ints. Observe que sua instrução switch deve ser exaustiva (usando defaultpalavras-chave ou todos os valores possíveis em instruções case).


2

A resposta de @missingfaktor é realmente correta, mas um pouco complicada demais. O código é mais detalhado (pelo menos para intervalos contínuos) do que poderia ser, e requer sobrecargas / conversões e / ou parametrização para long, float, Integer etc.

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
Desculpe pela resposta tardia, não estou conseguindo acompanhar as respostas do SO ultimamente. Vou responder à sua crítica uma por uma. 1. O código é um pouco mais detalhado, sim, e também faz alguns cálculos redundantes. Mas essa IMO está ajudando a clareza neste caso. Em seu código, é preciso lembrar os casos anteriores quando se cai pela escada.
missingfaktor de

2
Quanto a exigir sobrecargas e moldes, essa não é uma crítica válida da IMO. O fato de Java não ter uma maneira sensata de abstrair os tipos numéricos diz mais sobre as limitações do Java do que sobre essa abordagem. Você pode querer explorar a Numclasse de tipo de Haskell se quiser entender melhor este ponto.
missingfaktor de

@missingfaktor 1. Acho que é uma questão de gosto. Ao lidar com intervalos contínuos, pessoalmente gosto de pensar em ifs como se eu estivesse gradualmente extraindo o subfaixa inferior. Além disso, a computação redundante não é um problema de todos, mas a comparação redundante é um pouco preocupante: ele fica mais simples de fazer comparações adjacentes fora de sincronia, por exemplo, depois de algum edit: if (isBetween(x, 1, 4)) {...} else if (isBetween(x, 6, 10)). Enfim, não é grande coisa.
Alexander Malakhov

2. Concordo - limitação do Java, mas é com isso que temos que trabalhar e seu código original será duplicado para vários tipos. Nesse caso específico, um problema pode ser atenuado usando a classe Number , embora a abordagem de Haskell seja melhor (nunca aprendi realmente Haskell, mas se assemelha a "Concepts Light" de C ++. Além disso, acredito que você quis dizer Real, não Num).
Alexander Malakhov

2

Use uma NavigableMapimplementação, como 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);

Excelente solução! não se esqueça de verificar se há nulo usando if (messages.floorEntry(value)!=null) se você usar um valor inteiro
Ch Vas

1
@ChVas A forma como o mapa está configurado aqui, floorEntry()nunca irá retornar null(inserir a Integer.MIN_VALUEchave tem o objetivo de evitar isso). Mas, independentemente do seu tipo de valor, você precisa decidir como lidar com as chaves fora do intervalo válido.
erickson

0

Para número de entrada no intervalo 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

Este tipo de comportamento não é compatível com Java. No entanto, se você tiver um grande projeto que precise disso, considere combinar o código Groovy em seu projeto. O código Groovy é compilado em código de bytes e pode ser executado com JVM. A empresa para a qual trabalho usa Groovy para escrever classes de serviço e Java para escrever todo o resto.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.