【问题标题】:Use an array as a case statement in switch在 switch 中使用数组作为 case 语句
【发布时间】:2014-01-06 17:45:01
【问题描述】:

我正在尝试做这样的事情,即在 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;
}

【问题讨论】:

  • 我认为最好的方法是使用 if 语句并避免 switch
  • 我认为你应该问问自己为什么你的执行应该依赖于boolean 数组。也许一个类可以使用具有更多语义(并且更容易测试)的方法更好地包含这些数据?正如您所写,它看起来像是未来的维护噩梦。
  • 使用循环根据数组索引在 int 上设置位,然后关闭它。
  • 你想要的叫做“模式匹配”,但是在Java中你做不到。
  • 听起来您正在尝试实现位标志 - 请参阅此问题:Implementing a bitfield using java enums

标签: java arrays switch-statement


【解决方案1】:

@sᴜʀᴇsʜ ᴀᴛᴛᴀ 是对的。但我想补充一点。从 Java 7 开始,switch 语句支持字符串,所以你可以用它做点什么。 真的很脏,我不推荐,但这样可以:

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。这会很慢。另外,我认为您不应该以 any 方式 parse toString 输出。
  • 创意和糟糕的同时。 +1
  • 太受限于数组的大小,如果数组要改变大小,维护起来是多么的一团糟。
  • 对表演有这种无所谓的态度。通过在int 中设置位,这种类型的问题非常普遍地解决了——但您转换并比较了字符串在 Java 中
  • @bobobobo:你是对的。我永远不会为自己写这个。这只是一段有趣的代码,非常接近 OP 想要的。我知道这很可怕,相信我:)
【解决方案2】:

,你不能。

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

【讨论】:

  • 就我而言,这是正确的,尽管在其他答案中做出了一些很好的努力以获得良好的解决方案。
  • 老实说,这是最好的答案。我不确定应该鼓励解决原始问题所需的体操类型。
  • 这真的很老了,我什至不记得最初的问题是什么,但回顾代码似乎我做了一些糟糕的架构决策。这就是为什么我认为这是正确答案的原因——它更多的是做错事的信号,而不是找到解决方案。
【解决方案3】:

您不能打开整个阵列。但是您可以以牺牲switch 本身的可读性为代价转换为位集:

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

并在您的案例陈述中使用 二进制文字case 0b0101 是您的第一个。

【讨论】:

  • 哇,这当然是可能的,但这是解决实际问题的好方法吗?
  • 为了使其可扩展,我将它放在一个循环中:int switchVal = 0; for (int i = 0; i < values.length(); i++) { switchVal += values[i] ? (2 << i) : 0; },然后打开switchVal 变量。
  • 为了使开关更具可读性/意义,请考虑将这些二进制文字中的每一个都设为有意义的常量,以便您打开常量,而不是二进制文字。
  • 这基本上是将[true, false, true, false]转换为1010的一系列位,可以switched on,对吧?绝对是 IMO 的必经之路。
  • 我想展示一个在 Java SE 8 中使用 Lambda 的二进制示例,但不确定我是否会因此而受到抨击。我会等到三月。
【解决方案4】:

试试这个解决方案:

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

请参阅文档here

【讨论】:

  • 这是一个对数组大小非常严格的解决方案,一旦数组大小发生变化,您将面临维护的噩梦。
  • 他们将函数命名为isEquals??
  • 不需要 apache-commons-lang - 只需使用 JDK 中的java.util.Arrays.equals(arr1,arr2)
  • @drigoangelo:我知道我知道。但我想知道为什么他们不只使用isEqual,而是使用verb adjectiveverb noun,而不是verb verb。就个人而言,我更喜欢adjective 用于布尔函数,verb 用于改变状态的过程。
  • @JonathanDrapeau,无论如何,这种设计你可能会遇到维护噩梦......
【解决方案5】:

是的,您可以将数组传递给开关。问题是我说的不是 Java 数组,而是一种数据结构。

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

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

示例

这种机制的一个流行实现是 Linux 文件权限。您将rwx 作为“标志数组”。

如果整个数组为真,您将看到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 sn-p:

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

要了解有关使用二进制格式的更多信息,请查看以下问题:In Java, can I define an integer constant in binary format?

性能

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

要求尽可能高效的 C 程序使用这种机制;他们使用单个位表示的标志。

【讨论】:

    【解决方案6】:

    不,您不能,但是您可以将上面的代码替换为以下(我承认很脏)代码:

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

    【讨论】:

    • 我会将 sum 公式放入方法中,而不是直接放入 switch 语句中。
    • 除了它是错误的丑陋之外,它是错误的:0010 被解释为八进制......
    • @Alex 辅助方法应该是private ;) 另外return int 也不是一个好主意,因为对于大数组,这可能会溢出。
    • 我喜欢这个,尽管在接受的答案中维护与庞大的 if/else 语句相比似乎更容易
    • 另外,如果可以values[0]?1000:0,为什么还要(values[0]?1:0)*1000
    【解决方案7】:

    如果您试图确定一组条件是否为真,我会改用按位字段。

    例如,

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

    【讨论】:

      【解决方案8】:

      我会根据布尔数组中元素的序列计算一个值,即[true, false, true, true] 将计算为 1011,然后根据这个整数值,您可以使用 switch 语句。

      【讨论】:

      • 如果您想避免重写每个“case”语句,请确保如果您更改数组的长度(或顺序),它仍然有效。如果 Java 不允许对每个“案例”进行函数调用,则需要一个庞大的 if 链,可能每个备选案例都有一个关联数组,每个 if 测试都有一个函数调用。
      【解决方案9】:

      答案是否定的。最好的解释是学习如何使用the switch statement

      【讨论】:

      • Switch 语句只接受 int、chars、bytes、shorts 和 enum 类型。做到这一点的“正确”方法要么是一个大的 if..else 块,要么以某种方式将布尔数组呈现为一个整数并采取相应的行动。也许将其视为位掩码?
      • Java 开关也带字符串
      • @Bathsheba For Java7+。
      【解决方案10】:

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

      • 假设values.length &lt;= 64

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

      • Switch 与十六进制 magic numbers

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

      【讨论】:

        【解决方案11】:

        这个答案不是 Java,而是 Haxe,因为它是可能的,这要归功于模式匹配并且具有有趣的输出,这可能有助于您找到一个可以满足您要求的开关为了。数组可以按固定长度匹配。

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

        演示: 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 输出如何更改以进行高效切换。

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

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

        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

        【讨论】:

          【解决方案12】:

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

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

          【讨论】:

            【解决方案13】:

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

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

            相关代码覆盖在Lines 877Lines 982

            【讨论】:

              【解决方案14】:

              @Todor 是的,这在 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;
              }
              

              这里我只是将一个数组转换为字符串格式,然后在switch case中进行匹配。

              【讨论】:

                【解决方案15】:

                我会使用表示布尔状态的常量 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 文字,例如十六进制

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

                另一种方法可能是使用 java BitSetMap 映射到应该根据 bitset 的值执行的逻辑。

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2011-08-15
                  • 2010-09-26
                  • 2017-09-06
                  • 2012-07-26
                  • 1970-01-01
                  • 2016-03-25
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多