78

I am trying to do something like this, i.e., use an array in a switch statement. Is it possible in Java? If it isn't, please explain a possible solution.

boolean[] values = new boolean[4];

values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;

switch (values) {
    case [true, false, true, false]:
        break;
    case [false, false, true, false]:
        break;
    default:
        break;
}
Joel
  • 4,732
  • 9
  • 39
  • 54
Todor Grudev
  • 1,513
  • 2
  • 14
  • 25
  • 5
    I think best is to use an if statement and avoid the switch – Mukul Goel Dec 18 '13 at 13:53
  • 56
    I think that you should ask yourself why your execution should depend on a `boolean` array. Maybe a class could better contain this data with methods that have more semantics (and are more easily tested)? As you have written it, it looks like a future maintenance nightmare. – Eric Wilson Dec 18 '13 at 13:56
  • in my opinion ALL the above solutions are horrible, because switch/case should't be used with array. anyway, why not hashing? – Alessio Dec 18 '13 at 15:29
  • 1
    Use a loop to set bits on an int based on the array index, and switch off that. – octal9 Dec 18 '13 at 15:49
  • 15
    What you want is called "pattern matching", but you can't do it in Java. – Ingo Dec 18 '13 at 16:07
  • 11
    It sounds like you are trying to implement bit flags - see this question: [Implementing a bitfield using java enums](http://stackoverflow.com/questions/5346477/implementing-a-bitfield-using-java-enums) – T. Kiley Dec 18 '13 at 20:13
  • 1
    Make sure you leave well-documented comments describing what is happening in this portion of your code. Even when you yourself come back to it in a week you'll have to read through it a bit to figure out where you want to make changes. – DoubleDouble Dec 18 '13 at 21:54
  • Assume the array length <= 64, convert array to a `long` representing bitflags, switch against hexadecimal *magic numbers*! – recursion.ninja Dec 20 '13 at 16:42

15 Answers15

73

@sᴜʀᴇsʜ ᴀᴛᴛᴀ is right. But I wanted to add something. Since Java 7, switch statements support Strings, so you could do something with that. It is really dirty and I do not recommend, but this works:

boolean[] values = new boolean[4];

values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;

switch (Arrays.toString(values)) {
    case "[true, false, true, false]":
        break;
    case "[false, false, true, false]":
        break;
    default:
        break;
}

For those concerned about performance: you are right, this is not super fast. This will be compiled into something like this:

String temp = Arrays.toString(values)
int hash = temp.hashCode();
switch (hash)
{
    case 0x23fe8da: // Assume this is the hashCode for that
                    // original string, computed at compile-time
        if (temp.equals("[true, false, true, false]"))
        {

        }
        break;
    case 0x281ddaa:
        if (temp.equals("[false, false, true, false]"))
        {

        }
        break;

    default: break;
}
Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287
  • I'm pretty neutral +-1. It would be very slow. Also I didn't think you were supposed to *parse* `toString` output in *any* way. – Bathsheba Dec 18 '13 at 14:04
  • My initial thought was bitwise operations then this but I don't like it as a solution. – Todor Grudev Dec 18 '13 at 14:04
  • 77
    Creative and terrible at the same time. +1 – David Dec 18 '13 at 14:05
  • 3
    Too restricted to the size of the array, if the array as to change in size, what a mess it is to maintain. – Jonathan Drapeau Dec 18 '13 at 14:08
  • @Bathsheba: You are right, I added some explanation on how terrible the performance is. – Martijn Courteaux Dec 18 '13 at 14:13
  • "It is really dirty and I do not recommend" - I think this answer needs to start with that statement, and it needs to be turned into a header. But still, +1 for the dirty, hacky work-around. – Mike G Dec 18 '13 at 14:46
  • This is hilarious! I think its brilliant, but may get you fired at a job. Great for [my] research code, though! – Tommy Dec 19 '13 at 04:32
  • Remember, usually code is written once and read many times... This is not very readable (Also up-voted David's comment) – major-mann Dec 19 '13 at 10:15
  • Pardon me if this sounds rude, but for me, this represents everything that is wrong with Java programming. The upvotes represent what is wrong with the Java community. – bobobobo Dec 19 '13 at 13:45
  • 1
    There is this blasé attitude towards performance. This type of problem is _very commonly resolved_ with setting bits in an `int` -- yet you convert to and compare _strings_. _In Java_. – bobobobo Dec 19 '13 at 13:46
  • 1
    @bobobobo: You are right. I will never write this for my own. This was just a funny piece of code that lies very close to what the OP wanted. I do know that this is horrible, trust me :) – Martijn Courteaux Dec 25 '13 at 19:10
67

NO, simply you cannot.

SwitchStatement:
    switch ( Expression ) SwitchBlock

The type of the Expression must be char, byte, short, int, Character, Byte, Short, Integer, String, or an enum type (§8.9), or a compile-time error occurs.

http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.11

Suresh Atta
  • 120,458
  • 37
  • 198
  • 307
  • This is correct as far as I'm concerned, though some good efforts in the other answers for a good solution. – Code Whisperer Dec 18 '13 at 19:22
  • 8
    Honestly this is the best answer. I'm not sure the type of gymnastics required to make the original problem work should be encouraged. – NotMe Dec 18 '13 at 22:46
  • This is really old and I can't even remember what was the initial problem, but looking back at the code it seems that I've done some bad architectural decision. This is why I think this is the correct answer - it is more of a signal of doing something wrong rather than finding a solution. – Todor Grudev Mar 31 '17 at 08:53
49

You can't switch on whole arrays. But you could convert to a bit set at the expense of some readability of the switch itself:

switch (values[0] + 2 * values[1] + 4 * values[2] + 8 * values[3])

and use binary literals in your case statements: case 0b0101 is your first one.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Wow, this is certainly possible, but is that a good solution to the real problem? – Eric Wilson Dec 18 '13 at 13:58
  • 5
    To make this extendable, I'd put it in a loop: `int switchVal = 0; for (int i = 0; i < values.length(); i++) { switchVal += values[i] ? (2 << i) : 0; }` and then switch on the `switchVal` variable. – Darrel Hoffman Dec 18 '13 at 15:47
  • 3
    And to make the switch more readable/meaningful, consider making each of those binary literals a meaningful constant so you are switching on constants, not binary literals. – rrauenza Dec 18 '13 at 19:21
  • 3
    This is basically converting `[true, false, true, false]` to the series of bits `1010`, which can be `switch`ed on, right? Definitely the way to go IMO. – Izkata Dec 18 '13 at 21:33
  • I wanted to show a binary example using Lambda in Java SE 8 but wasn't sure if I would get flamed for that. I'll wait until March. – ialexander Dec 18 '13 at 23:29
  • +1 and @Eric: sure, in spirit this is a great solution to the original problem, although not the generalized version of that problem. The OP has an array of booleans. Converting it to a bitset is not only useful in general, but valuable for this problem. Certainly it could be more legible though (e.g. use a function). – Travis Wilson Dec 19 '13 at 05:19
46

Try this solution:

    boolean[] values = new boolean[4];
    values[0] = true;
    values[1] = false;
    values[2] = false;
    values[3] = true;

    if (ArrayUtils.isEquals(values, new boolean[] {true, false, true, false})) {
    ...
    }
    else if (ArrayUtils.isEquals(values, new boolean[] {false, false, true, false})) {
    ...
    }
    else {
    ...
    }

See docs here.

Alex
  • 11,451
  • 6
  • 37
  • 52
  • 50
    This is a solution really tight up to the size of the array, the moment the array as to change in size, you'll have a maintenance nightmare. – Jonathan Drapeau Dec 18 '13 at 14:09
  • 99
    They named a function `isEquals`?? – Sebastian Mach Dec 18 '13 at 15:59
  • 1
    @phresnel "is" is the code pattern for methods that return a boolean value. The method tests if two arrays are equals, so it seems ok to me. – drigoangelo Dec 18 '13 at 16:49
  • 1
    @drigoangelo Yes, that's true, but the rest of the Java library seems to follow simply `Object.equals(Object)` as in the case of String equality. – Kyle Falconer Dec 18 '13 at 17:49
  • 10
    There's no need for apache-commons-lang - just use `java.util.Arrays.equals(arr1,arr2)` from the JDK. – Ed Staub Dec 18 '13 at 17:59
  • 10
    @drigoangelo: I know I know. But I wonder why they didn't just use `isEqual`, so either `verb adjective` or `verb noun`, instead of `verb verb`. Personally, I just prefer `adjective` for boolean functions, and `verb` for procedures that change state. – Sebastian Mach Dec 18 '13 at 18:24
  • 2
    @JonathanDrapeau, you'll likely have a maintenance nightmare with this kind of design anyway... – MEMark Dec 18 '13 at 18:41
  • 2
    Doesn't `Arrays.equals` do this without requiring a 3rd-party dependency? – user2357112 Dec 18 '13 at 21:49
  • @phresnel the naming follows the convention. equals can only be used on an instance. ArrayUtils, as all **Utils classes provides static method. – Julien Dec 18 '13 at 22:35
  • Coming in fresh looking at Java questions with little Java knowledge but 10yrs+ php background, I nealy puked when I saw this! I respect the code, mind you. I'm not saying this to flame your post. Switch statements are obviously more dynamic in a scripting language but I fail to see why there is no decent support for this construct in the language. Everything is just way more low-level and it amazes me how people can actually memorize all these complicated hacks to emulate a simple puny switch statement that works pretty nice in php with any size array or int or string etc... My 2 cents. – Stephane Gosselin Dec 19 '13 at 05:25
  • @phresnel Yes, isEqual really makes more sense. – drigoangelo Dec 19 '13 at 13:50
  • `ArrayUtils.isEquals` can handle multi-dimensional arrays correctly (see docs above), so it's better to have one solution for everything. – Alex Dec 19 '13 at 13:52
22

Yes, you can pass an array to a switch. The catch is that I'm not talking about Java arrays, but a data structure.

An array is a systematic arrangement of objects, usually in rows and columns.

What you are trying to do is implement a system that recognizes different flags and depending on the flags that are turned on or off you take different actions.

Example

A popular implementation of such mechanism is Linux file permissions. Where you have rwx as the "array of flags".

If the whole array is true, you'll see rwx, which means that you have all the permissions. If you are not allowed to perform any action on a file, the whole array is false, you'll see ---.

Implementation

Guess what, you can think of integers as arrays. An integer is represented by an "array of bits".

001 // 1, if on, set x 
010 // 2, if on, set w 
100 // 4, if on, set r
// putting it all together in a single "array" (integer)
111 // 2^2 + 2^1 + 2^0 = 4 + 2 + 1 = 7

That is why the permission rwx can be represented as a 7

Java snippet:

class Flags {                                                                    
public static void main(String args[]) {         
        /** 
         * Note the notation "0b", for binary; I'm using it for emphasis.
         * You could just do: 
         * byte flags = 6;
         */                     
        byte flags = 0b110; // 6                     
        switch(flags) {                                                          
            case 0: /* do nothing */ break;                                      
            case 3: /* execute and write */ break;                       
            case 6: System.out.println("read and write\n"); break;         
            case 7: /* grant all permissions */ break;                           
            default:                                                             
                System.out.println("invalid flag\n");           
        }                                                                        
    }                                                                            
}

To know more about using a binary format, check this question: In Java, can I define an integer constant in binary format?

Performance

  • Saves memory
  • You don't have to do extra processing, switches or any other type of juggling.

C programs that require to be as efficient as possible use this type of mechanism; they use flags represented with single bits.

Community
  • 1
  • 1
givanse
  • 14,503
  • 8
  • 51
  • 75
21

No, you cannot, however you can replace the above with the following (dirty I admit) code:

boolean[] values = new boolean[4];

values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;

switch(makeSuitableForSwitch(values)) {
   case 1010: 
     break;
   case 10: 
     break;
   default:
     break;
} 

private int makeSuitableForSwitch( boolean[] values) {
    return (values[0]?1:0)*1000+(values[1]?1:0)*100+(values[2]?1:0)*10+(values[3]?1:0);
}
Adam Siemion
  • 15,569
  • 7
  • 58
  • 92
10

If you're trying to determine if a set of conditions is true, I'd use bitwise fields instead.

For example,

public class HelloWorld
{
  // These are the options that can be set.
  // They're final so treated as constants.
  static final int A=1<<0, B=1<<1, C=1<<2, D=1<<3 ;

  public static void main(String []args)
  {
    // Now I set my options to have A=true, B=true, C=true, D=false, effectively
    int options = A | B | C ;

    switch( options )
    {
      case (A):
        System.out.println( "just A" ) ;
        break ;
      case (A|B):
        System.out.println( "A|B" ) ;
        break ;
      case (A|B|C): // Final int is what makes this work
        System.out.println( "A|B|C" ) ;
        break ;
      default:
        System.out.println( "unhandled case" ) ;
        break ;
    }
  }
}
bobobobo
  • 64,917
  • 62
  • 258
  • 363
6

I'd compute a value based on the sequence of the elements in the boolean array, i.e. [true, false, true, true] would evaluate to 1011 and then based on this integer value you can use switch statement.

Juvanis
  • 25,802
  • 5
  • 69
  • 87
  • Make sure that if you change the length (or order) of the array, it still works, if you want to avoid a rewrite of every 'case' statement. If Java doesn't permit a function call for each 'case', a massive if chain will be required, possibly with an associative array for each alternative case and a function call for each if test. – Phil Perry Dec 18 '13 at 18:50
2

As of JRE 1.7, you will need to use a hack, I recommend:

  • Assume values.length <= 64

  • Convert values to a long representing bitflags

  • Switch against hexadecimal magic numbers

Java Code Hack:

if(values.length > 64)
  throw new IllegalStateException();

long bitflags = 0x0L;

for(int i=0; i< values.length; ++i)
  if(values[i])
    bitflags |= 0x01L << i;

switch(bitflags) {
  case 0xEL: // represents [true,  true,  true, false]
    break;
  case 0xAL: // represents [true,  false, true, false]
    break;
  case 0x2L: // represents [false, false, true, false]
    break;
  default:
    break;
}
recursion.ninja
  • 5,377
  • 7
  • 46
  • 78
2

This answer is not Java, but Haxe because it is possible in it, thanks to pattern matching and has interesting output, which might be useful for you to find a switch that does what you are asking for. Arrays can be matched on fixed length.

I created an demo that compiles to Javascript and Flash. You can see the js-output in the right column.

Demo: http://try.haxe.org/#86314

class Test {
  static function main(){

    var array=[true,false,true];

    var result=switch(array){
      case [true,true,false]: "no";
      case [true,false,true]: "yes";
      default:"??";
    }

    #if js
      new js.JQuery("body").html(result);
    #elseif flash
      trace(result);
    #end

    // ouputs: "yes"
  }
}

This is the outputted switch, it uses nested switches. If you play with the cases, you see how the js-ouput changes to have a efficient switch.

(function () { "use strict";
var Test = function() { };
Test.main = function() {
    var array = [true,false,true,false];
    var result;
    switch(array.length) {
    case 4:
        switch(array[0]) {
        case true:
            switch(array[1]) {
            case false:
                switch(array[2]) {
                case true:
                    switch(array[3]) {
                    case false:
                        result = "no";
                        break;
                    default:
                        result = "??";
                    }
                    break;
                default:
                    result = "??";
                }
                break;
            default:
                result = "??";
            }
            break;
        case false:
            switch(array[1]) {
            case false:
                switch(array[2]) {
                case true:
                    switch(array[3]) {
                    case false:
                        result = "yes";
                        break;
                    default:
                        result = "??";
                    }
                    break;
                default:
                    result = "??";
                }
                break;
            default:
                result = "??";
            }
            break;
        }
        break;
    default:
        result = "??";
    }
    new js.JQuery("body").html(result);
};
var js = {};
var q = window.jQuery;
js.JQuery = q;
Test.main();
})();

Another interesting pattern that you can use underscores. a _ pattern matches anything, so case _: is equal to default, which makes you able to do this:

var myArray = [1, 6];
var match = switch(myArray) {
    case [2, _]: "0";
    case [_, 6]: "1";
    case []: "2";
    case [_, _, _]: "3";
    case _: "4";
}
trace(match); // 1

http://haxe.org/manual/pattern_matching#array-matching

Mark Knol
  • 9,663
  • 3
  • 29
  • 44
1

The answer is NO. The best explain is learn how to use the switch statement.

Sergi
  • 579
  • 3
  • 14
  • Switch statements only take ints, chars, bytes, shorts, and enum types. The "right" way to do it would be either a big if..else block, or to somehow render the array of booleans down to an integer and act accordingly. Maybe treat it as a bitmask? – David Dec 18 '13 at 13:54
  • 1
    Java switches also take strings – Bathsheba Dec 18 '13 at 14:07
  • 2
    @Bathsheba For Java7+. – Maroun Dec 18 '13 at 14:12
1

Here is another approach requiring no imports nor libraries:

boolean[] values = new boolean[4];

values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;

int mask = buildMask(values);

if (areEquals(mask, true, false, true, false)) {
    // ...
} else if (areEquals(mask, false, false, true, false)) {
    // ...
} else {
    // ...
}

private int buildMask(boolean... values) {
    int n = 0;
    for (boolean b : values) {
        n = (n << 1) | (b ? 1 : 0);
    }
    return n;
}

private boolean areEquals(int mask, boolean... values) {
    return mask == buildMask(values);
}
Stephan
  • 41,764
  • 65
  • 238
  • 329
1

You can also take a look at how Groovy implements the isCase() methods in Java, use a simpler version that fits your needs. It's possible to put that in an interface and create a DSL to compare any two object in your application.

return isCase(DefaultTypeTransformation.asCollection(caseValue), switchValue);

The relevant code is covered in Lines 877 through Lines 982

rgamed
  • 11
  • 1
1

@Todor Yes, THIS IS POSSIBLE IN JAVA.

boolean[] values = new boolean[4];

values[0] = true;
values[1] = false;
values[2] = false;
values[3] = true;

String values_asString = Arrays.toString(values);
 
switch (values_asString) {
    case "[true, false, true, false]":
        break;
    case "[false, false, true, false]":
        break;
    case "[true, false, false, true]":
        System.out.println("YAAAAAAAAAA GOT IT");
        break;
    default:
        break;
}

Here I just converted an array to a string format and then matched it in the switch case.

Kleysley
  • 505
  • 3
  • 12
jatinkumar patel
  • 2,920
  • 21
  • 28
0

I would use constant int values that represent the boolean state.

If you use Java 1.7 or above you can use binary literals that are more readable.

public static final int TRUE_FALSE_TRUE_FALSE = 0b1010;
public static final int FALSE_FALSE_TRUE_FALSE = 0b0010;

for Java 1.6 and below use any other int literals, e.g. hex

public static final int TRUE_FALSE_TRUE_FALSE = 0xA;
public static final int FALSE_FALSE_TRUE_FALSE = 0x2;

then create a method that converts a boolean array to an integer bitset. E.g.

public static int toIntBitSet(boolean...values){
    int bitset = 0;
    for (boolean value : values) {
       bitset = (bitset << 1) | (value ? 1 : 0);
    }
    return bitset;
}

Finally use the constants in your switch statement

boolean[] values = new boolean[]{true, false, true, false};

int bitset = toIntBitSet(values);

switch (bitset) {
  case TRUE_FALSE_TRUE_FALSE:
    System.out.println(Integer.toBinaryString(bitset));
    break;
  case FALSE_FALSE_TRUE_FALSE:
    System.out.println(Integer.toBinaryString(bitset));
    break;
  default:
    break;
}

Another approach might be to use a java BitSet and a Map that maps to the logic that should be executed depending on the bitset's value.

public static void main(String[] args) throws Exception {
  Map<BitSet, Callable<String>> bitSetMap = new HashMap<>();

  bitSetMap.put(bitSetValueOf(true, false, true, false), new TrueFalseTrueFalseCallable());
  bitSetMap.put(bitSetValueOf(false, false, true, false), new FalseFalseTrueFalseCallable());

  boolean[] values = new boolean[]{true, false, true, false};

  BitSet bitset = bitSetValueOf(values);

  Callable<String> callable = bitSetMap.get(bitset);
  if (callable == null) {
    callable = new DefaultCallable();
  }

  String result = callable.call();
  System.out.println(result);
}

public static BitSet bitSetValueOf(boolean... values) {
   BitSet bitSet = new BitSet();
      for (int i = 0; i < values.length; i++) {
         bitSet.set(i, values[i]);
      }
   return bitSet;
}

and implement your logic

class FalseFalseTrueFalseCallable implements Callable<String> {

  @Override
  public String call() throws Exception {
    return "0010";
  }

}

class TrueFalseTrueFalseCallable implements Callable<String> {

  @Override
  public String call() throws Exception {
    return "1010";
  }

}

class DefaultCallable implements Callable<String> {

  @Override
  public String call() throws Exception {
    return "default value";
  }

}
René Link
  • 48,224
  • 13
  • 108
  • 140