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

发布于 2024-12-11 13:39:39 字数 467 浏览 0 评论 0原文

我有一个方法,可以使用 32 个 if-else 语句检查 5 个不同条件的所有组合(想想真值表)。这 5 个不同的字母表示每个方法对字符串运行自己的正则表达式,并返回一个布尔值,指示字符串是否与正则表达式匹配。例如:

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

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

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

}...etc,etc.

但是,它确实影响了我的应用程序的性能(抱歉,我无法透露太多细节)。谁能推荐一种更好的方法来处理这种逻辑?

每个使用正则表达式的方法如下所示:

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

谢谢!

I have a method that checks all of the combinations of 5 different conditions with 32 if-else statements (think of the truth table). 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. 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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

段念尘 2024-12-18 13:39:39

你可以尝试

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

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;
}
π浅易 2024-12-18 13:39:39

将每个条件表示为一个位标志,测试每个条件一次,然后在单个 int 中设置相关标志。然后打开 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)
  // ...
}

represent each condition as a bit flag, test each condition once, and set the relevant flag in a single int. then switch on the int value.

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)
  // ...
}
权谋诡计 2024-12-18 13:39:39

以上所有答案都是错误的,因为优化问题的正确答案是:测量!使用探查器来测量代码在何处花费了时间。

话虽如此,我敢打赌,最大的胜利是避免多次编译正则表达式。之后,正如其他人所建议的,只评估每个条件一次并将结果存储在布尔变量中。所以thait84有最好的答案。

我还准备打赌 jtahlborn 和 Peter Lawrey 以及 Salvatore Previti 的建议(本质上是相同的),虽然很聪明,但会给你带来微不足道的额外好处,除非你在 6502 上运行......

(这个答案读起来就像我充满了它,所以为了充分披露,我应该提到我实际上对优化没有希望,但测量仍然是正确的答案。)

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.

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

(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.)

云仙小弟 2024-12-18 13:39:39

在不了解更多细节的情况下,以最后执行执行“繁重”工作的方式排列 if 语句可能会有所帮助。这是假设其他条件为真,从而避免了“繁重”的提升。简而言之,如果可能的话,利用短路。

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

向地狱狂奔 2024-12-18 13:39:39

对每个字符串运行一次正则表达式,并将结果存储到布尔值中,然后对布尔值执行 if / else,而不是多次运行正则表达式。另外,如果可以的话,尝试重新使用正则表达式的预编译版本并重新使用它。

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. Also, if you can, try to re-use a pre-compiled version of your regex and re-use this.

抚笙 2024-12-18 13:39:39

一种可能的解决方案:使用创建二进制值的开关。

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

如果您可以避免切换并使用数组,那么速度会更快。

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.

開玄 2024-12-18 13:39:39

也许将其分为几层,如下所示:

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

尽管如此,感觉必须有更好的方法来做到这一点。

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 2024-12-18 13:39:39

我有一个 EnumSet 的解决方案。然而它太冗长了,我想我更喜欢@Peter Lawrey 的解决方案。

在 Bloch 的《Effective Java》中,建议在位字段上使用 EnumSet,但我在这里例外。尽管如此,我还是发布了我的解决方案,因为它对于遇到稍微不同问题的人来说可能很有用。

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

你这样使用它:

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

但这样会更有效:

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
}

I have a solution with EnumSet. However it's too verbose and I guess I prefer @Peter Lawrey's solution.

In Effective Java by Bloch it's recommended to use EnumSet over bit fields, but I would make an exception here. 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
}
一绘本一梦想 2024-12-18 13:39:39

您还可以将 if/else 调整为 switch/case (我理解这更快)

You could also adapt your if/else to a switch/case (which I understand is faster)

养猫人 2024-12-18 13:39:39

将 A、B、C、D 和 E 预先生成为布尔值,而不是在 if 条件块中评估它们,将提供可读性和性能。如果您还关心不同情况下的性能,您可以将它们组织为树或将它们组合成一个整数 (X = (A?1:0)|(B?2:0)|...|( E?16:0)) 您将在 switch 中使用。

pre-generating A,B,C,D and E as booleans rather than evaluating them in if conditions blocks would provide both readability and performance. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文