【问题标题】:Efficiently Check Multiple Conditions [closed]有效检查多个条件[关闭]
【发布时间】:2017-04-13 02:27:34
【问题描述】:

我遇到了需要检查多个条件的情况,其中每个组合都有不同的结果。在我的具体情况下,我有 2 个变量,它们是枚举类型,每个变量可以是 2 个不同的值。

enum Enum1
{
    COND_1,
    COND_2
}
enum EnumA
{
    COND_A,
    COND_B
}
Enum1 var1;
EnumA varA;

这给了我 4 种可能的条件,这需要 4 种不同的结果。我想出了几种不同的方法来做到这一点,无论是使用 if 语句还是 switch 语句:

if(var1 == Enum1.COND_1 && varA == EnumA.COND_A)
{
    // Code
}
else if(var1 == Enum1.COND_1 && varA == EnumA.COND_B)
{
    // Code
}
else if(var1 == Enum1.COND_2 && varA == EnumA.COND_A)
{
    // Code
}
else if(var1 == Enum1.COND_2 && varA == EnumA.COND_B)
{
    // Code
}

或者:

switch(var1)
{
    case COND_1:
        switch(varA)
        {
            case COND_A:
                // Code
                break;
            case COND_B:
                // Code
                break;
        }
        break;
    case COND_2:
        switch(varA)
        {
            case COND_A:
                // Code
                break;
            case COND_B:
                // Code
                break;
        }
        break;
}

我想到了其他人,但不想用代码填充它:P 我想知道最好的方法是什么。我认为 switch 更容易阅读,但 ifs 更短。我认为如果开关可以有多个条件会很酷,但我还没有听说过。这也引出了一个问题:对于任意数量的变量和可能的值,最好的方法是什么?

【问题讨论】:

  • 如果您以认真的方式执行此操作,那么您可能会使用规则引擎或其他将所有代码抽象出来的第三方系统。跨度>
  • 为什么需要检查多个这样的条件?你到底在做什么?这可能会通过重新设计以更好的方式解决,而不是尝试在 if 和 switch 之间做出决定。
  • 主要是设置一些变量。它们彼此独立,所以我没有看到比检查条件更优雅的解决方案。
  • 我亲自与其他一些程序员交谈过,他们都推荐嵌套开关。我会将其添加为答案,但由于问题是基于意见的,它们已被关闭。我很抱歉。感谢那些回答的人!

标签: java performance conditional


【解决方案1】:

我更喜欢没有嵌套的if 变体,因为它很短,而且你在一行中拥有所有条件。

在调试期间停止执行代码时,它可能会变得乏味,因为您必须跳过所有前面的条件,即 O(n)。执行代码时,这无关紧要,因为编译器可能会优化代码。

没有明显的最佳方法,因此您必须尝试一下。

【讨论】:

    【解决方案2】:

    这可能是个疯狂的想法,但您可以使用标志构造一个 int 或一个字节,并在单个开关中使用它。

    private int getIntegerStateForConditions(boolean... conditions ){
        int state = 0;
        int position = 0;
        for(boolean condition: conditions){
            if(condition){
                state = state || (1 << position++);
            }
        }
        return state;
    }
    

    ...

    switch(getIntegerStateForCondition((var1 == Enum1.COND_1), (var2 == EnumA.COND_A)){
        case 0: ... //both condition false
        case 1: ... //first condition true second false
        case 2: ... //first false, second true ...
    }
    

    ...

    我认为这远不是干净的代码,但看起来更好。

    【讨论】:

      【解决方案3】:

      如果我是你,我会依靠位标志来处理只有一个 byte(因为你只有 4 个用例),并在这个 byte 上使用 switch 语句来管理你的所有用例。

      类似这样的:

      private static final int COND_2 = 1;
      private static final int COND_B = 2;
      
      private byte value;
      
      public void setValue(Enum1 enum1) {
          if (enum1 == Enum1.COND_1) {
              this.value &= ~COND_2;
          } else {
              this.value |= COND_2;
          }
      }
      
      public void setValue(EnumA enumA) {
          if (enumA == EnumA.COND_A) {
              this.value &= ~COND_B;
          } else {
              this.value |= COND_B;
          }
      }
      
      public Enum1 getEnum1() {
          return (this.value & COND_2) == COND_2 ? Enum1.COND_2 : Enum1.COND_1;
      }
      
      
      public EnumA getEnumA() {
          return (this.value & COND_B) == COND_B ? EnumA.COND_B : EnumA.COND_A;
      }
      

      那么您的测试将是:

      switch (value) {
          case 0 :
              // 1-A;
              break;
          case 1 :
              // 2-A;
              break;
          case 2 :
              // 1-B;
              break;
          case 3 :
              // 2-B;
              break;
      }
      

      【讨论】:

        【解决方案4】:

        对于您的小用例,我可能会使用嵌套的 if 语句。但是,如果您有大量 enum 常量,那么使用流的模式可能会使您的代码更易于阅读和维护(以降低性能)。你可以使用这样的流来解决它:

        Stream.of(new Conditional(COND_1, COND_A, () -> {/* do something */}),
                  new Conditional(COND_1, COND_B, () -> {/* do something */}),
                  new Conditional(COND_2, COND_A, () -> {/* do something */}),
                  new Conditional(COND_2, COND_B, () -> {/* do something */}))
              .filter(x -> x.test(var1, varA))
              .findAny()
              .ifPresent(Conditional::run);
        

        这需要一个支持类:

        class Conditional implements BiPredicate<Enum1, EnumA>, Runnable
        {
            private final Enum1 var1;
            private final EnumA varA;
            private final Runnable runnable;
        
            public Conditional(Enum1 var1, EnumA varA, Runnable runnable) {
                this.var1 = var1;
                this.varA = varA;
                this.runnable = runnable;
            }
        
            @Override
            public boolean test(Enum1 enum1, EnumA enumA) {
                return var1 == enum1 && varA == enumA;
            }
        
            @Override
            public void run() {
                runnable.run();
            }
        }
        

        【讨论】:

          【解决方案5】:

          我个人更喜欢这个:

          if(understandableNameInContextName1(var1, varA))
          {
              // Code
          }
          else if(understandableNameInContextName2(var1, varA))
          {
              // Code
          }
          else if(understandableNameInContextName3(var1, varA))
          {
              // Code
          }
          else if(understandableNameInContextName4(var1, varA))
          {
              // Code
          }
          
          private boolean understandableNameInContextName1(Object var1, Object varA){
           return (var1 == Enum1.COND_1 && varA == EnumA.COND_A);
          }
          
          private boolean understandableNameInContextName2(Object var1, Object varA){
           return (var1 == Enum1.COND_1 && varA == EnumA.COND_B);
          }
          
          private boolean understandableNameInContextName3(Object var1, Object varA){
           return (var1 == Enum1.COND_2 && varA == EnumA.COND_A);
          }
          
          private boolean understandableNameInContextName4(Object var1, Object varA){
           return (var1 == Enum1.COND_2 && varA == EnumA.COND_B);
          }
          

          方法的名称可能是 isOrderShippedAndDelivered()、isRequestSendAndAckRecieved()。

          原因是这将使代码更具可读性。 除非你有数据可以引导你回到这些 if 语句,否则优化这些不会有太大的收获。

          见: https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil

          【讨论】:

            【解决方案6】:

            这里的性能差异可能可以忽略不计,因此我将重点关注简短性和可读性。所以我会通过使用临时变量来简化if

            boolean is_1 = (var1 == Enum1.COND_1);
            boolean is_A = (varA == EnumA.COND_A);
            
            if(is_1 && is_A)
            {
                // Code
            }
            else if(is_1 && !is_A)
            {
                // Code
            }
            else if(!is_1 && is_A)
            {
                // Code
            }
            else if(!is_1 && !is_A)
            {
                // Code
            }
            

            【讨论】:

              【解决方案7】:

              我绝对更喜欢平面版本,它可以使用更少的重复:

              // If you can't make the variables final, make some final copies
              final Enum1 var1 = Enum1.COND_2;
              final EnumA varA = EnumA.COND_B;
              
              class Tester {  // You could also make an anonymous BiPredicate<Enum1, EnumA>
                  boolean t(Enum1 v1, EnumA vA) {
                      return var1 == v1 && varA == vA;
                  }
              };
              
              Tester tes = new Tester();
              
              if (tes.t(Enum1.COND_1, EnumA.COND_A)) {
                  // code
              } else if (tes.t(Enum1.COND_1, EnumA.COND_B)) {
                  // code
              } else if (tes.t(Enum1.COND_2, EnumA.COND_A)) {
                  // code
              } else if (tes.t(Enum1.COND_2, EnumA.COND_B)) {
                  // code
              }
              

              运行它here。您可以通过 static import of the enums 避免提及枚举名称来使其更短且更少冗余,例如tes.t(COND_1, COND_B)。或者,如果您愿意放弃一些编译时安全性,您可以传递一个字符串,该字符串将转换为两个枚举值,例如tes.t("COND_1 COND_A")(实现留给读者)。

              【讨论】:

                【解决方案8】:

                这取决于代码的复杂性和组合的数量,但另一种选择是字典,其键包含枚举元组和代码委托的值。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2012-09-03
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-04-19
                  • 2014-10-22
                  • 2018-03-31
                  • 1970-01-01
                  相关资源
                  最近更新 更多