有效地检查多个条件

时间:2016-11-29 07:26:31

标签: java performance conditional

我有一种情况需要检查多种情况,其中每种组合都有不同的结果。在我的具体情况下,我有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我想知道最好的方法是做什么。我认为开关更容易阅读,但ifs更短。我认为如果开关有多种条件会很酷,但我还没有听说过。这也引出了一个问题:使用任意数量的变量和可能的值,最好的方法是什么?

8 个答案:

答案 0 :(得分:9)

对于您的小用例,我可能会选择嵌套的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();
    }
}

答案 1 :(得分:7)

这里的性能差异可能微不足道,所以我会关注简洁性和可读性。所以我只想通过使用临时变量来简化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
}

答案 2 :(得分:3)

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

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

没有明显的最好方法,所以你需要进行一些实验。

答案 3 :(得分:3)

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

// 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")(实现留给读者)。

答案 4 :(得分:1)

也许是疯狂的想法,但你可以使用标志构造一个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 ...
}

...

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

答案 5 :(得分:1)

如果我是你,我将依赖位标志,以便只有一个byte(因为你只有4个用例)来处理并在{{1}上使用switch语句管理所有用例。

这样的事情:

byte

然后你的测试将是:

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

答案 6 :(得分:1)

我个人更喜欢这个:

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

答案 7 :(得分:0)

取决于代码的复杂程度和组合的数量,但另一个选项是一个字典,其中的键包含枚举的元组和代码的委托值。