简体   繁体   English

Java中许多if-else语句的性能不佳

[英]Poor performance of many if-else statements in Java

I have a method that checks all of the combinations of 5 different conditions with 32 if-else statements (think of the truth table). 我有一个方法,用32个if-else语句检查5种不同条件的所有组合(想想真值表)。 The 5 different letters represent methods that each run their own regular expressions on a string, and return a boolean indicating whether or not the string matches the regex. 5个不同的字母表示每个在字符串上运行自己的正则表达式的方法,并返回一个布尔值,指示字符串是否与正则表达式匹配。 For example: 例如:

if(A,B,C,D,E){

}else if(A,B,C,D,!E){

}else if(A,B,C,!D,!E){

}...etc,etc.

However, it is really affecting the performance of my application (sorry, I can't go into too many details). 但是,它确实影响了我的应用程序的性能(对不起,我不能详细介绍)。 Can anyone recommend a better way to handle such logic? 谁能推荐一种更好的方法来处理这种逻辑?

Each method using a regular expression looks like this: 使用正则表达式的每个方法如下所示:

String re1 = "regex here";
Pattern p = Pattern.compile(re1, Pattern.DOTALL);
Matcher m = p.matcher(value);
return m.find();

Thanks! 谢谢!

You can try 你可以试试

boolean a,b,c,d,e;
int combination = (a?16:0) + (b?8:0) + (c?4:0) + (d?2:0) + (e?1:0);
switch(combination) {
   case 0:
        break;
   // through to
   case 31:
        break;
}

represent each condition as a bit flag, test each condition once, and set the relevant flag in a single int. 将每个条件表示为位标志,测试每个条件一次,并在单个int中设置相关标志。 then switch on the int value. 然后打开int值。

int result = 0;
if(A) {
  result |= 1;
}
if(B) {
  result |= 2;
}
// ...

switch(result) {
  case 0: // (!A,!B,!C,!D,!E)
  case 1: // (A,!B,!C,!D,!E)
  // ...
}

All the above answers are wrong, because the correct answer to an optimisation question is: Measure! 以上所有答案都是错误的,因为优化问题的正确答案是:测量! Use a profiler to measure where your code is spending its time. 使用分析器来衡量代码花费时间的位置。

Having said that, I'd be prepared to bet that the biggest win is avoiding compiling the regexes more than once each. 话虽如此,我还是准备打赌,最大的胜利是避免每次编译正则数据集。 And after that, as others suggested, only evaluate each condition once and store the results in boolean variables. 之后,正如其他人所建议的那样,只评估每个条件一次并将结果存储在布尔变量中。 So thait84 has the best answer. 所以thait84有最好的答案。

I'm also prepared to bet jtahlborn and Peter Lawrey's and Salvatore Previti suggestions (essentially the same), clever though they are, will get you negligible additional benefit, unless you're running on a 6502... 我也准备打赌jtahlborn和Peter Lawrey以及Salvatore Previti的建议(基本上是相同的),尽管它们很聪明,但是你会得到微不足道的额外好处,除非你在6502上运行...

(This answer reads like I'm full of it, so in the interests of full disclosure I should mention that I'm actually hopeless at optimisation. But measuring still is the right answer.) (这个答案看起来就像我已经充满了它,所以为了充分披露,我应该提到我实际上在优化方面毫无希望。但测量仍然是正确的答案。)

Without knowing more details, it might be helpful to arrange the if statements in such a way that the ones which do the "heavy" lifting are executed last. 在不知道更多细节的情况下,以这样的方式安排if语句可能会有所帮助,即执行“重”提升的语句最后执行。 This is making the assumption that the other conditionals will be true thereby avoiding the "heavy" lifting ones all together. 这假设其他条件将成立,从而避免“重”提升一起。 In short, take advantage of short-circuits if possible. 简而言之,尽可能利用短路。

Run the regex once for each string and store the results in to booleans and just do the if / else on the booleans instead of running the regex multiple times. 为每个字符串运行一次正则表达式并将结果存储到布尔值中,然后在布尔值上执行if / else,而不是多次运行正则表达式。 Also, if you can, try to re-use a pre-compiled version of your regex and re-use this. 此外,如果可以,请尝试重新使用正则表达式的预编译版本并重新使用它。

One possible solution: use a switch creating a binary value. 一种可能的解决方案:使用创建二进制值的开关。

int value = (a ? 1 : 0) | (b ? 2 : 0) | (c ? 4 : 0) | (d ? 8 : 0) | (e ? 16 : 0);

switch (value)
{
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    ...
    case 31:
}

If you can avoid the switch and use an array it would be faster. 如果你可以避免切换并使用数组,它会更快。

Maybe partition it into layers, like so: 也许将它分成几层,如下所示:

if(A) {
    if(B) {
        //... the rest
    } else {
        //... the rest
    }
} else {
    if(B) {
        //... the rest
    } else {
        //... the rest
    }
}

Still, feels like there must be a better way to do this. 不过,感觉必须有更好的方法来做到这一点。

I have a solution with EnumSet. 我有一个EnumSet的解决方案。 However it's too verbose and I guess I prefer @Peter Lawrey's solution. 然而,它太冗长了,我想我更喜欢@Peter Lawrey的解决方案。

In Effective Java by Bloch it's recommended to use EnumSet over bit fields, but I would make an exception here. 在Bloch的Effective Java中,建议在位字段上使用EnumSet,但我会在这里做一个例外。 Nonetheless I posted my solution because it could be useful for someone with a slightly different problem. 尽管如此,我发布了我的解决方案,因为它可能对问题略有不同的人有用。

import java.util.EnumSet;

public enum MatchingRegex {
  Tall, Blue, Hairy;

  public static EnumSet<MatchingRegex> findValidConditions(String stringToMatch) {
     EnumSet<MatchingRegex> validConditions = EnumSet.noneOf(MatchingRegex.class);
     if (... check regex stringToMatch for Tall)
       validConditions.add(Tall);
     if (... check regex stringToMatch for Blue)
       validConditions.add(Blue);
     if (... check regex stringToMatch for Hairy)
       validConditions.add(Hairy);
     return validConditions;         
  }
}

and you use it like this: 你像这样使用它:

Set<MatchingRegex> validConditions = MatchingRegex.findValidConditions(stringToMatch);

if (validConditions.equals(EnumSet.of(MatchingRegex.Tall, MathchingRegex.Blue, MatchingRegex.Hairy))
   ...
else if (validConditions.equals(EnumSet.of(MatchingRegex.Tall, MathchingRegex.Blue))
   ...
else if ... all 8 conditions like this

But it would be more efficient like this: 但它会像这样更有效:

if (validConditions.contains(MatchingRegex.Tall)) {
  if (validConditions.contains(MatchingRegex.Blue)) {
     if (validConditions.contains(MatchingRegex.Hairy)) 
        ... // tall blue hairy
     else
        ... // tall blue (not hairy)
  } else {
     if (validConditions.contains(MatchingRegex.Hairy)) 
        ... // tall (not blue) hairy
     else
        ... // tall (not blue) (not hairy)
} else {
      ... remaining 4 conditions
}

你也可以调整你的if / else到一个开关/盒子(据我所知更快)

pre-generating A,B,C,D and E as booleans rather than evaluating them in if conditions blocks would provide both readability and performance. 预先生成A,B,C,d和E以布尔而不是在评价他们if条件块将提供既可读性和性能。 If you're also concerned about performance the different cases, you may organise them as a tree or combine them into a single integer (X = (A?1:0)|(B?2:0)|...|(E?16:0)) that you'd use in a switch . 如果您还关注性能不同的情况,您可以将它们组织成树或将它们组合成一个整数(X =(A?1:0)|(B?2:0)| ... |( E?16:0))你在switch使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM