Using switch statement with a range of value in each case?
Asked Answered
P

21

113

In Java, is it possible to write a switch statement where each case contains more than one value? For example (though clearly the following code won't work):

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

I think this can be done in Objective C, are there a similar thing in Java? Or should I just use if, else if statements instead?

Protoxylem answered 3/6, 2012 at 20:11 Comment(3)
There even extensions in plain C that allow for this.Fresher
In that case, maybe if/else is a better solution.Ashmore
Not today, but the Java folks are exploring options for this alongside Pattern Matching in switch expressions (borrowing heavily from Scala, I would say). Search "guard" in this document which captures possible ideas: cr.openjdk.java.net/~briangoetz/amber/switch-rehab.htmlArioso
I
7

after reading all the comments I didn't see anybody mention enhanced switch in which you can have multiple values in one case like this ->

switch(value){
   case 1,2,3,4:
      //dosth
      break;
   case 7,9,10,23:
      //dosth
      break;
}

and since in your case, there is only one expression in every case, you can do the following without the need to break every case->

switch (value) {
    case 1, 2, 3, 4 -> System.out.println("one of 1,2,3,4 matched");
    case 7, 9, 10, 23 -> System.out.println("one of 7,9,10,23 matched");
}

this is one of the many added benefits with enhanced switches in java.

Interline answered 17/5, 2021 at 13:31 Comment(1)
This is supported in jdk 14+Disentomb
F
117

Java has nothing of that sort. Why not just do the following?

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");
}
Felicle answered 3/6, 2012 at 20:49 Comment(5)
This works great and is simple. Also if you want to select numbers not in the range all you need is if(!isBetween... , good job.Impulsion
@Felicle I've criticized your answer a bit. So, you would probably want to reply something. +1 though.Tuque
@MCEmperor, please don't edit my answer with your personal formatting preferences. That's not a useful edit.Felicle
@Felicle Sorry. But that edit was approved a long time ago; I didn't even remember I had ever done that.Nissy
Weird that switch statements seem almost useless in most languages I've come across, not sure why they don't develop them a bit more. Even something simple like a range is strangely not possible.Sonatina
D
82

The closest you can get to that kind of behavior with switch statements is

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 if statements.

Douce answered 3/6, 2012 at 20:12 Comment(3)
I'm almost certain that's exactly what he's trying to avoid.Sonatina
@Sonatina which is why I recommend using if statements instead :)Douce
Well then a comment would've been better like Eric Wang above.Sonatina
C
36

other alternative is using math operation by dividing it, for example:

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

But, as you can see this can only be used when the range is fixed in each case.

Codger answered 28/9, 2013 at 1:0 Comment(1)
This works nicely with http status codes divided by 100.Bartel
D
22

I know this post is old but I believe this answer deserves some recognition. There is no need to avoid the switch statement. This can be done in java but through the switch statement, not the cases. It involves using ternary operators.

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;
        }
    }
}
Despain answered 4/3, 2018 at 16:0 Comment(6)
Nice solution! With Java 7 and higher you could also switch on a String so your cases are even more self-documenting, e.g., switch ((1 <= num && num <= 5 ) ? "1 .. 5" : ... and then case "1 .. 5":.Syllepsis
@DanielWiddis: I would suggest using an enum instead of strings, to ensure consistency.Strictly
@Strictly Generally agree. I was trying to permit the use of the "1 .. 5" style syntax.Syllepsis
@DanielWiddis: Certainly a good thing; I would name the enum constants something like FROM_1_TO_5. Usually the ranges have a certain meaning, and in that case the enum constants can be named according to the meaning of the range.Strictly
I think we can agree either option is better than 0, 1, or 2.Syllepsis
Although in a critical loop, I would expect this to be non-performant. Each ternary operator compiles to an if-branch statement and then there's another in the actual switch, so you end up having double the number of if-branches taking place in the compiled byte code. The string solution also relies on expensive string comparisons. enum comparisons, at least are cheaper.Octahedron
D
15

I don't think you can do that in Java. Best bet is to just put the code in the last case of the range.

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:
     //
}
Dualpurpose answered 3/6, 2012 at 20:14 Comment(0)
M
12
  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"); 
Missal answered 30/7, 2014 at 6:32 Comment(0)
T
11

Java 12+

It is supported as of Java 12. Check out JEP 354. No "range" possibilities here, but can be useful either.

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

You should be able to implement that on ints too. Note through that your switch statement have to be exhaustive (using default keyword, or using all possible values in case statements).

Java 20+

UPDATE: As of Java 20 (March '23) this is possible! Check out JEP 433. Their example is if you want to test for a triangle of a certain size.

switch (s) {
        case null -> 
            { break; }
        case Triangle t
        when t.calculateArea() > 100 ->
            System.out.println("Large triangle");
        default ->
            System.out.println("A shape, possibly a small triangle");
    }
Trainbearer answered 30/4, 2019 at 3:3 Comment(1)
Thanks so much for updating old questions!! Links to JEP help a lotLucilelucilia
I
7

after reading all the comments I didn't see anybody mention enhanced switch in which you can have multiple values in one case like this ->

switch(value){
   case 1,2,3,4:
      //dosth
      break;
   case 7,9,10,23:
      //dosth
      break;
}

and since in your case, there is only one expression in every case, you can do the following without the need to break every case->

switch (value) {
    case 1, 2, 3, 4 -> System.out.println("one of 1,2,3,4 matched");
    case 7, 9, 10, 23 -> System.out.println("one of 7,9,10,23 matched");
}

this is one of the many added benefits with enhanced switches in java.

Interline answered 17/5, 2021 at 13:31 Comment(1)
This is supported in jdk 14+Disentomb
E
6

Try this if you must use 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;
    }
}
Emulate answered 8/1, 2016 at 5:27 Comment(4)
can you please give explanation too .. ?Phosphaturia
The function range return some value according to the input argument, so the "range of value" detection could be done here, coupled with switch case clause.Emulate
This is worked for my use case, I have 2 int variable and I want to check is that 2 variable are in same range like (10-20),(20-30),.... like thisReflex
I would suggest using an enum instead of magic int values.Strictly
O
6

Or you could use your solo cases as intended and use your default case to specify range instructions as :

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;   
}
Outrelief answered 16/4, 2016 at 8:27 Comment(1)
This is what I always do, but also add a comment in between cases, eg.: // 10 - 15 see below defaultCongelation
C
5

Use a NavigableMap implementation, like 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);
Cassatt answered 26/5, 2016 at 19:37 Comment(2)
Excellent solution! don't forget to check for null using if (messages.floorEntry(value)!=null) if you use an Integer valueHuguenot
@ChVas The way the map is set up here, floorEntry() will never return null (inserting the Integer.MIN_VALUE key is intended to prevent that). But, regardless of your value type, you need to decide how to handle keys outside the valid range.Cassatt
A
4

No you can't do that. The best you can do is that

case 1:
case 2:
case 3:
case 4:
case 5: 
  System.Out.Println("testing case 1 to 5");
break;
Aphelion answered 3/6, 2012 at 20:14 Comment(0)
D
4

It's possible to group several conditions in the same case statement using the mechanism of fall through allowed by switch statements, it's mentioned in the Java tutorial and fully specified in section §14.11. The switch Statement of the Java Language Specification.

The following snippet of code was taken from an example in the tutorial, it calculates the number of days in each month (numbered from month 1 to month 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;
}

As you can see, for covering a range of values in a single case statement the only alternative is to list each of the possible values individually, one after the other. As an additional example, here's how to implement the pseudocode in the question:

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;
}
Delimitate answered 3/6, 2012 at 20:15 Comment(0)
L
4

You could use an enum to represent your ranges,

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;
  }
}
Lockhart answered 22/12, 2013 at 3:24 Comment(0)
S
3

Here is a beautiful and minimalist way to go

(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");
Sergu answered 17/1, 2016 at 18:57 Comment(3)
Can you explain this?? -- i do get the 'mini ifs' but other than that idk.Featured
@Tunaki this is a if..elseif alternative using in cascade the ternary operator(condition ? statementIfTrue : statementIfFalse).Sergu
num > 7 && num < 8 Can you think of an integer that is both greater than seven and also less than eight? There isn't one. This code doesn't work.Rissa
T
2

@missingfaktor 's answer is indeed correct but a bit over-complicated. Code is more verbose (at least for continuous intervals) then it could be, and requires overloads/casts and/or parameterization for 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");
Tuque answered 18/5, 2014 at 9:20 Comment(4)
Sorry for the late response, I am not able to keep up with SO responses lately. I'll reply to your critique one by one. 1. Code is a bit more verbose, yes, and it also does some redundant computation. But that IMO is aiding clarity in this case. In your code, one has to remember the previous cases as one falls through the ladder.Felicle
As for requiring overloads and casts, that's not a valid criticism IMO. That Java has no sensible way of abstracting over numeric types says about Java's limitations, more than about this approach. You may want to explore Haskell's Num type-class if you wish to understand this point better.Felicle
@Felicle 1. I guess that's a matter of taste. When dealing with continuous intervals I personally like to think of ifs as if I'm gradually spilling off lower subrange. Further, a redundant computation isn't a problem at all, but redundant comparison is a bit worrying: it gets simpler to make adjacent comparisons out of sync, e.g. after some edit: if (isBetween(x, 1, 4)) {...} else if (isBetween(x, 6, 10)). Anyway, not a big deal.Tuque
2. Agreed - Java's limitation, but that's what we have to work with and your original code will be duplicated for various types. In this particular case a problem can be mitigated by using Number class, though Haskell's approach is better (never came to really learn Haskell, but it resembles "Concepts Light" of C++. Also, I believe you meant Real, not Num).Tuque
D
1

For input number in range 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);
Deepseated answered 16/4, 2019 at 6:56 Comment(0)
T
1

Apart from switch statements, since Java 14 SE there are switch expressions you can use as an alternative when each case produces some result. If handling a non-integer Math.round() can be used, for example.

String result = switch (Math.round(value)) {
    case 1, 2, 3, 4, 5:
        // handle case 1
        yield "result of case 1";
    case 6, 7, 8, 9, 10:
        // handle case 2
        yield "result of case 2";
    default:
        // handle the default case
        yield "result of the default case";
}

System.out.println("result = " + result);

The difference from the switch statement is that all cases must be covered (often with the default case).

Transverse answered 9/11, 2022 at 20:49 Comment(0)
P
0

This type of behavior is not supported in Java. However, if you have a large project that needs this, consider blending in Groovy code in your project. Groovy code is compiled into byte code and can be run with JVM. The company I work for uses Groovy to write service classes and Java to write everything else.

Photocompose answered 28/4, 2019 at 20:39 Comment(0)
D
0

Excellent contributions. In case it may be still helpful, I found an option to analyze whether inside a string there are characters different from letters and spaces:

string name = scEntry.nextLine();
int len = name.length(); 
int s = 0;
do { 
     int asc = name.codePointAt(s);
     switch ((asc == 32) ? 0 : (64 < asc && asc < 91) ? 1 : 
             (96 < asc && asc < 123) ? 2 : 99) {
         case 0, 1, 2 -> {
                  s++; 
                  break;
         }
         case 99 -> {
           s = 0;
           System.out.println("Type in only letters [A-Za-z]
                             or spaces");
           System.out.print("Type the user name again: ");
           name = scEntry.nextLine();
           len = name.length();
           break;
         }
} while (s < len);
s = 0;

In this example, the letters' ASCII codes along with the "space" ASCII code are grouped and represented by the numbers 0, 1 or 2. ASCII codes different from the above mentioned, are grouped in the value "99", so I can inform any thing about them to the user.

Deice answered 1/8, 2021 at 1:9 Comment(0)
N
0

With Java 20 (JEP 433 Pattern Matching for switch (Fourth Preview)) this feature is available.

Integer integer = ...;
switch (integer) {
    case Integer i when i > 0 && i <= 5 -> System.out.println("testing case 1 to 5");
    case Integer i when i > 5 && i <= 10 -> System.out.println("testing case 6 to 10");
    default -> System.out.println("All the remaining integers");
}
Ninetieth answered 7/12, 2023 at 13:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.