Używasz instrukcji switch z zakresem wartości w każdym przypadku?


94

Czy w Javie można napisać instrukcję switch, w której każdy przypadek zawiera więcej niż jedną wartość? Na przykład (chociaż wyraźnie poniższy kod nie zadziała):

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

Myślę, że można to zrobić w Objective C, czy jest coś podobnego w Javie? Czy mam po prostu użyć if, else ifoświadczenia zamiast?


Istnieją nawet rozszerzenia w zwykłym C, które na to pozwalają.
arshajii

W takim razie może if/elsejest lepszym rozwiązaniem.
Eric Wang

Odpowiedzi:


100

Java nie ma nic takiego. Dlaczego po prostu nie wykonać następujących czynności?

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
To działa świetnie i jest proste. Również jeśli chcesz wybrać liczby spoza zakresu, wystarczy, jeśli (! IsBetween ..., dobra robota.
a54studio

1
@missingfaktor Trochę skrytykowałem twoją odpowiedź. Więc prawdopodobnie chciałbyś coś odpowiedzieć. Jednak +1.
Alexander Malakhov

@MCEmperor, nie edytuj mojej odpowiedzi zgodnie ze swoimi osobistymi preferencjami dotyczącymi formatowania. To nie jest przydatna zmiana.
missingfaktor

@missingfaktor Przepraszamy. Ale ta zmiana została zatwierdzona dawno temu; Nawet nie pamiętałem, że kiedykolwiek to robiłem.
MC Emperor

77

Najbliższe tego rodzaju zachowania można uzyskać za pomocą switchstwierdzeń

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

Użyj ifinstrukcji.


30

Inną alternatywą jest użycie operacji matematycznej przez podzielenie jej, na przykład:

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

Ale, jak widać, można tego używać tylko wtedy, gdy zakres jest ustalony w każdym przypadku.


6
Działa to dobrze z kodami stanu http podzielonymi przez 100.
Stefan

14

Myślę, że nie możesz tego zrobić w Javie. Najlepiej jest po prostu umieścić kod w ostatnim przypadku zakresu.

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

Wiem, że ten post jest stary, ale uważam, że ta odpowiedź zasługuje na uznanie. Nie ma potrzeby unikania instrukcji switch. Można to zrobić w Javie, ale za pomocą instrukcji switch, a nie przypadków. Obejmuje użycie operatorów trójskładnikowych.

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
Niezłe rozwiązanie! W Javie 7 i nowszych wersjach można także włączyć String, aby Twoje przypadki były jeszcze bardziej samodokumentujące, np. switch ((1 <= num && num <= 5 ) ? "1 .. 5" : ...A potem case "1 .. 5":.
Daniel Widdis

@DanielWiddis: Sugerowałbym użycie wyliczenia zamiast ciągów, aby zapewnić spójność.
nmatt

@nmatt Ogólnie się zgadzam. Próbowałem zezwolić na użycie składni w stylu „1 .. 5”.
Daniel Widdis

2
@DanielWiddis: Z pewnością dobra rzecz; Nazwałbym stałe wyliczeniowe coś w rodzaju FROM_1_TO_5. Zwykle zakresy mają określone znaczenie iw takim przypadku stałe wyliczeniowe można nazwać zgodnie ze znaczeniem zakresu.
nmatt

1
Myślę, że możemy się zgodzić, że każda opcja jest lepsza niż 0, 1 lub 2.
Daniel Widdis

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

Lub możesz użyć swoich przypadków solo zgodnie z przeznaczeniem i użyć domyślnego przypadku, aby określić instrukcje dotyczące zakresu jako:

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

To jest to, co zawsze robię, ale dodam też komentarz pomiędzy przypadkami, np .: // 10-15 patrz poniżej default
Perdi Estaquel

5

Spróbuj tego, jeśli musisz użyć przełącznika.

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

czy możesz też udzielić wyjaśnień ..?
user1140237

Zakres funkcji zwraca pewną wartość zgodnie z argumentem wejściowym, więc można tutaj przeprowadzić wykrywanie "zakresu wartości", w połączeniu z klauzulą ​​przełącznika przypadku.
zl9394

To działa dla mojego przypadku użycia, mam 2 zmienne int i chcę sprawdzić, czy 2 zmienne są w tym samym zakresie, jak (10-20), (20-30), .... tak
Sameer Kazi

Sugerowałbym użycie enum zamiast magicznych wartości int.
nmatt

4

Nie, nie możesz tego zrobić. Najlepsze, co możesz zrobić, to to

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

4

Oto piękna i minimalistyczna droga

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

Czy możesz to wyjaśnić? - dostaję „mini ifs”, ale inne niż ten idk.
Kamin Pallaghy

@Tunaki jest to if..elseifalternatywa wykorzystująca kaskadowo operator trójskładnikowy ( condition ? statementIfTrue : statementIfFalse).
Williem

3

Możliwe jest zgrupowanie kilku warunków w tej samej caseinstrukcji przy użyciu mechanizmu fall through dozwolonego przez instrukcje switch, jest to wspomniane w samouczku Java i szczegółowo opisane w sekcji §14.11. Instrukcja switch z języka Java Specification .

Poniższy fragment kodu został zaczerpnięty z przykładu w samouczku, oblicza liczbę dni w każdym miesiącu (numerowane od 1 do 12 miesięcy):

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

Jak widać, casejedyną alternatywą dla pokrycia zakresu wartości w pojedynczym zestawieniu jest wyszczególnienie każdej z możliwych wartości indywidualnie, jedna po drugiej. Jako dodatkowy przykład, oto jak zaimplementować pseudokod w pytaniu:

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

Możesz użyć znaku enumdo reprezentowania swoich zakresów,

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

Jest obsługiwany od wersji Java 12. Sprawdź JEP 354 . Brak możliwości „zakresu”, ale mogą być też przydatne.

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

Powinieneś być w stanie zaimplementować to również na int. Zwróć uwagę, że instrukcja switch musi być wyczerpująca (używając defaultsłowa kluczowego lub wszystkich możliwych wartości w instrukcjach case).


2

Odpowiedź @missingfaktor jest rzeczywiście poprawna, ale nieco przesadzona. Kod jest bardziej rozwlekły (przynajmniej dla ciągłych interwałów) niż mógłby być i wymaga przeciążeń / rzutów i / lub parametryzacji dla long, float, Integer itp.

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
Przepraszam za późną odpowiedź, ostatnio nie jestem w stanie nadążyć za odpowiedziami SO. Odpowiem po kolei na twoją krytykę. 1. Kod jest nieco bardziej rozwlekły, tak, a także wykonuje pewne zbędne obliczenia. Ale ta IMO sprzyja jasności w tym przypadku. W swoim kodzie trzeba pamiętać o poprzednich przypadkach, gdy ktoś spada po drabinie.
missingfaktor

2
Jeśli chodzi o wymaganie przeciążeń i rzutów, to nie jest uzasadniona krytyka IMO. To, że Java nie ma rozsądnego sposobu abstrahowania od typów liczbowych, mówi o ograniczeniach Javy, a nie o tym podejściu. Możesz zbadać Numklasę typów Haskella, jeśli chcesz lepiej zrozumieć ten punkt.
missingfaktor

@missingfaktor 1. Myślę, że to kwestia gustu. Kiedy mam do czynienia z ciągłymi interwałami, osobiście lubię myśleć o nich iftak, jakbym stopniowo wydzielał dolny zakres. Ponadto, zbędne obliczenia nie jest to problem w ogóle, ale zbędny porównanie jest nieco niepokojąca: robi się prostsze, aby sąsiadujące ze sobą porównywać z synchronizacji, na przykład po pewnym EDIT: if (isBetween(x, 1, 4)) {...} else if (isBetween(x, 6, 10)). Zresztą nic wielkiego.
Alexander Malakhov

2. Zgoda - ograniczenie Javy, ale z tym musimy pracować, a Twój oryginalny kod zostanie zduplikowany dla różnych typów. W tym konkretnym przypadku problem można złagodzić za pomocą klasy Number , chociaż podejście Haskella jest lepsze (nigdy nie nauczył się Haskella, ale przypomina "Concepts Light" w C ++. Również myślę, że Realnie Num).
Alexander Malakhov

2

Użyj NavigableMapimplementacji, na przykład 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);

Doskonałe rozwiązanie! nie zapomnij sprawdzić wartości null, używając if (messages.floorEntry(value)!=null) wartości całkowitej
Ch Vas

1
@ChVas Sposób, w jaki mapa jest tutaj ustawiona, floorEntry()nigdy nie powróci null(włożenie Integer.MIN_VALUEklucza ma temu zapobiec). Ale niezależnie od typu wartości, musisz zdecydować, jak obsługiwać klucze spoza prawidłowego zakresu.
erickson

0

Dla numeru wejścia z zakresu 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

Ten typ zachowania nie jest obsługiwany w języku Java. Jeśli jednak masz duży projekt, który tego potrzebuje, rozważ dodanie kodu Groovy do swojego projektu. Kod Groovy jest kompilowany do kodu bajtowego i może być uruchamiany za pomocą JVM. Firma, dla której pracuję, używa Groovy do pisania klas usług i Javy do pisania wszystkiego innego.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.