使用数组作为switch中的case语句


76

我试图做这样的事情,即在switch语句中使用数组。Java可能吗?如果不是,请说明可能的解决方案。

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

5
我认为最好是使用if语句并避免切换
Mukul Goel 2013年

56
我认为您应该问自己为什么执行应该依赖boolean数组。也许类可以使用具有更多语义(并且更容易测试)的方法更好地包含此数据?如您所写,这似乎是未来维护的噩梦。
埃里克·威尔逊

1
使用循环基于数组索引在int上设置位,然后关闭该位。
octal9 2013年

15
您想要的被称为“模式匹配”,但是您无法在Java中做到这一点。
Ingo 2013年

11
听起来您正在尝试实现位标志-请参阅以下问题:使用Java枚举实现位域
T. Kiley 2013年

Answers:


65

,您根本不能。

SwitchStatement:
    switch ( Expression ) SwitchBlock

表达式的类型必须为char,byte,short,int,Character,Byte,Short,Integer,String或枚举类型(第8.9节),否则会发生编译时错误。

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


就我而言,这是正确的,尽管在其他答案中需要做出一些努力才能找到一个好的解决方案。
窃窃私语者

8
老实说,这是最好的答案。我不确定应该鼓励进行原始问题工作的体操类型。
NotMe 2013年

这确实很老,我什至不记得最初的问题是什么,但是回头看一下代码,看来我做了一些错误的体系结构决策。这就是为什么我认为这是正确的答案的原因-它更多是表明在做错事情而不是找到解决方案。
Todor Grudev

73

@sᴜʀᴇsʜ是正确的。但是我想补充一点。从Java 7开始,switch语句支持Strings,因此您可以执行一些操作。它真的很脏,我不建议这样做,但是可以这样做:

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

对于那些关心性能的人:您是对的,这不是超级快。这将被编译成这样的东西:

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

我相当中立+ -1。这将非常慢。而且我不认为您应该以任何方式解析 toString输出。
Bathsheba 2013年

72
同时具有创造力和可怕性。+1
David

3
太限制了数组的大小,如果数组的大小改变了,那将是多么混乱。
乔纳森·德拉波

1
这种对绩效的无礼态度。这种类型的问题通常可以通过在中设置位来解决int但是您可以转换并比较字符串在Java中
bobobobo

1
@bobobobo:你是对的。我永远不会自己写这个。这只是一段有趣的代码,非常接近OP的需求。我确实知道这太可怕了,请相信我:)
Martijn Courteaux 2013年

50

您无法打开整个阵列。但是您可以转换为一些设置,但会牺牲一些switch本身的可读性:

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

并在case语句中使用二进制文字:这case 0b0101是您的第一个。


哇,这当然是可能的,但这是解决实际问题的好方法吗?
埃里克·威尔逊

5
为了使它可扩展,我将其放入循环中:int switchVal = 0; for (int i = 0; i < values.length(); i++) { switchVal += values[i] ? (2 << i) : 0; }然后打开switchVal变量。
Darrel Hoffman 2013年

3
为了使开关更具可读性/意义,请考虑使这些二进制文字中的每一个成为有意义的常量,以便您启用常量,而不是二进制文字。
rrauenza

3
基本上,这是转换[true, false, true, false]1010可以switch编辑的位系列,对吗?绝对是走IMO的道路。
Izkata

我想展示一个在Java SE 8中使用Lambda的二进制示例,但是不确定是否会为此而烦恼。我等到三月。
ialexander 2013年

47

试试这个解决方案:

    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 {
    ...
    }

在这里查看文档。


50
这实际上是一个严格限制阵列大小的解决方案,一旦阵列大小发生变化,您将面临维护的噩梦。
乔纳森·德拉波

98
他们命名了一个函数isEquals??
塞巴斯蒂安·马赫

10
不需要apache-commons-lang-只需java.util.Arrays.equals(arr1,arr2)在JDK中使用即可。
Ed Staub

10
@drigoangelo:我知道我知道。但我不知道他们为什么不直接使用isEqual,因此无论是verb adjectiveverb noun代替verb verb。就个人而言,我只喜欢adjective布尔函数和verb改变状态的过程。
塞巴斯蒂安·马赫

2
@JonathanDrapeau,无论如何,您可能会对这种设计产生维护噩梦……
MEMark

22

是的,您可以将数组传递给交换机。问题是我不是在谈论Java数组,而是一种数据结构。

数组是对象的系统排列,通常按行和列排列。

您试图做的是实现一个识别不同标志的系统,并根据打开或关闭的标志采取不同的操作。

这种机制的一种流行实现是Linux文件权限。您在哪里拥有rwx“标志数组”。

如果整个数组为true,则将看到rwx,这意味着您拥有所有权限。如果不允许您对文件执行任何操作,则整个数组为false,您将看到---

实作

猜猜是什么,您可以将整数视为数组。整数由“位数组”表示。

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

这就是为什么rwx可以将许可表示为7

Java代码段:

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

要了解有关使用二进制格式的更多信息,请检查以下问题:在Java中,我可以定义二进制格式的整数常量吗?

性能

  • 节省内存
  • 您不必进行额外的处理,切换或任何其他类型的杂耍。

需要尽可能高效的C程序使用这种类型的机制;它们使用以单个位表示的标志。


21

不,您不能,但是可以用以下(我承认是肮脏的)代码替换以上代码:

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

3
我将求和公式放入方法中,而不是直接放在switch语句中。
乔纳森·德拉波

8
除了错误的事实外,这是错误的:0010被解释为八进制...
Gyro Gearless 2013年

2
@Alex helper方法应该是private;)也不是一个好主意,return int因为对于大型数组,这可能会溢出。
Maroun 2013年

1
我喜欢这样,如果在接受的答案中维持/
反对

1
另外,为什么(values[0]?1:0)*1000可以values[0]?1000:0呢?
Cruncher 2013年

10

如果您要确定一组条件是否为真,那么我将使用按位字段。

例如,

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

6

我会根据布尔数组中元素的顺序来计算一个值,即[true, false, true, true]计算结果为1011,然后基于此整数值,可以使用switch语句。


如果要避免重写每个“ case”语句,请确保如果更改数组的长度(或顺序),它仍然可以工作。如果Java不允许对每个“ case”进行函数调用,则将需要庞大的if链,可能对每个替代案例都具有关联数组,并对每个if test进行函数调用。
Phil Perry

2

答案是不。最好的解释是学习如何使用switch语句


Switch语句仅采用int,char,byte,short和enum类型。“正确”的方法要么是大的if..else块,要么是将布尔数组简化为整数并采取相应措施。也许将其视为位掩码?
大卫,

1
Java的交换机还采取串
拔示巴

2
@Bathsheba对于Java7 +。
Maroun 2013年

2

从JRE 1.7开始,您将需要使用hack,我建议:

  • 承担 values.length <= 64

  • 将值转换为long代表位标志

  • Switch针对十六进制魔术数

Java代码破解:

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

2

这个答案不是Java,而是Haxe,因为它可以实现,这要归功于模式匹配并具有有趣的输出,这可能对您找到符合您要求的开关很有用。数组可以匹配固定长度。

我创建了一个可编译为Javascript和Flash的演示。您可以在右列中看到js-output。

演示: 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"
  }
}

这是输出的开关,它使用嵌套开关。如果您处理这些案例,您将了解js-ouput如何进行更改以实现高效切换。

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

您可以使用下划线另一个有趣的模式。_模式匹配任何内容,因此大小写_:等于默认值,这使您能够做到这一点:

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


1

这是不需要导入也不需要库的另一种方法:

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

1

您还可以查看Groovy如何在Java中实现isCase()方法,并使用适合您需求的更简单版本。可以将其放在接口中并创建DSL以比较应用程序中的任何两个对象。

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

相关的代码覆盖线877通过线982


1

@Todor是的,这在JAVA中是可能的。

boolean[] values = new boolean[4];

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

values = Arrays.toString(values)

switch (values) {
    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;
}

注意:我不是Java开发人员,所以我的代码语法可能是错误的,但是逻辑是完美的。您可以编辑我的答案。在这里,我只是尝试将数组转换为字符串格式,然后在切换时匹配。


0

我将使用表示布尔状态的常量int值。

如果您使用Java 1.7或更高版本,则可以使用更具可读性的二进制文字。

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

对于Java 1.6及以下版本,请使用其他int文字,例如hex

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

然后创建一个将布尔数组转换为整数位集的方法。例如

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

最后在switch语句中使用常量

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

另一种方法可能是使用JavaBitSet和Java ,Map它根据位集的值映射到应执行的逻辑。

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

并实施你的逻辑

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

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.