什么更好:多个“if”语句还是一个带有多个条件的“if”语句?

66

我的工作需要开发一个小型Java应用程序,用于解析非常大的XML文件(~300k行)以选择非常特定的数据(使用 Pattern ),因此我正在尝试对其进行一些优化。 我想知道以下两个片段哪个更好:

if (boolean_condition && matcher.find(string)) {
    ...
}

或者

if (boolean_condition) {
    if (matcher.find(string)) {
        ...
    }
}

其他细节:

  • 这些 if 语句在循环内的每次迭代中执行(大约 20k 次迭代)
  • boolean_condition 是一个布尔值,它在每次迭代中使用外部函数计算而来
  • 如果 boolean 设置为 false,我就不需要测试正则表达式是否匹配

谢谢您的帮助。


2
没有简单的答案。请查看关于相同话题的这个 SO 讨论 - Adriaan Koster
完全没有性能差异。这只是一个风格问题,因此它是一个观点问题。 - Charles Duffy
10个回答

75

我遵循的一个黄金法则是尽可能"避免嵌套"。但如果以此为代价使我的单个 if 条件语句过于复杂,我不介意进行嵌套。

此外,您正在使用短路&&运算符。因此,如果布尔值为假,则它甚至不会尝试匹配!

所以,

if (boolean_condition && matcher.find(string)) {
    ...
}

这是正确的方法!


1
谢谢!我不确定运算符是否设置为false,匹配是否会执行。 - 3rgo
12
考虑 if(x != null && x.isY()); 如果 && 没有短路评估,那么当 x == null 时,这将在你的面前爆炸。 - user
1
是的确实!但我必须确定,所以我问了。 - 3rgo
+adarshr 好的,即使在嵌套的 if 语句中也会进行短路计算,因为如果外部 if 语句的结果为 false,则不会检查内部 if 语句,与 && 运算符相同,如果第一个表达式的结果为 false,则会进行短路计算。 - Shreyan Mehta
@user 在评估之前,您难道不应该检查是否为空吗?常识告诉我们是的?也许是ArgumentNullException? - D3vtr0n
如果您在空语句上使用多个操作,那么是的。但如果它类似于树的叶节点,则简单条件(或类似于C#中的空传播)就足够了。 - Ferazhu

24

以下两种方法:

public void oneIf(boolean a, boolean b)
{
    if (a && b)
    {   
    }
}

public void twoIfs(boolean a, boolean b)
{
    if (a)
    {
        if (b)
        {       
        }
    }
}

使用第一种写法和第二种写法在方法体中生成的字节码完全相同,因此不会有任何性能差异,这只是一种风格上的选择(个人偏好第一种写法)。


7
如果方法 public void noIfs(boolean a, boolean b) {} 也能产生相同的字节码,我不会感到惊讶的。;-) - Axel
2
@Axel 不,它不会(我刚刚检查过了!)这并不奇怪,Java编译器几乎不进行任何优化,而是留给JVM在运行时进行。 - Jonathan
我认为在第二个结构中,解析器需要做更多的工作,因为它必须弄清楚下一条语句是“if”还是其他内容。另一方面,在第一个结构中,它只需解析条件,并知道接下来的部分是条件的一部分。 - kvaibhav

7

两种方式都可以,如果第一个条件为假,则不会测试第二个条件。

使用使代码更可读和易懂的方式。对于仅有两个条件的情况,第一种方式更加逻辑清晰和易读。但是如果有5或6个条件使用 &&, ||! 进行连接时,情况可能就不同了。


4

我建议将您的表达式提取到一个语义有意义的变量中,然后将其传递给您的评估。而不是:

if (boolean_condition && matcher.find(string)) { ... }

将表达式赋值给一个变量,然后计算该变量:

const hasItem = boolean_condition && matcher.find(string)

if (hasItem) { ... }

使用这种方法,即使是最复杂的评估也可以保持可读性:

const hasItem = boolean_condition && matcher.find(string)

const hasOtherThing = boolean_condition || boolean_condition

const isBeforeToday = new Date(string) < new Date()

if (hasItem && hasOtherThing && isBeforeToday) { ... }

如果你想要构建比最基本的软件更复杂的任何东西,那么这就是每次唯一正确的答案。 - BallisticPugh

3

Java使用短路运算符处理布尔运算符,因此这两种变体在功能上是相同的。因此,如果boolean_condition为false,则不会继续匹配。

最终,你需要选择更易于阅读和调试的方式,但如果最终括号数量太多,则深度嵌套可能会变得难以控制。

如果条件变得更长,一种改善可读性的方法是将其拆分成多行:

if(boolean_condition &&
   matcher.find(string))
{
    ...
}

在那时,唯一的选择就是将 && 和 || 放在前一行的结尾或当前行的开头。

3
如果您想符合Sonar规则squid:S1066,您应该折叠if语句以避免警告,因为它指出:

可折叠的“if”语句应该合并


3

我经常看到太多的&&和||被串在一起形成逻辑混乱,往往是微妙错误的源头。

很容易就会认为在正确的位置加入另一个&&或||,从而破坏现有的逻辑。

因此,作为一个通用规则,我尽量不使用它们,以避免随着需求的变化加入更多的内容。


2

第一个建议是尽量避免这样的if嵌套,我认为这是不好的风格/丑陋的代码,并且&&会短路,只有在布尔值为真时才会测试matcher.find()。


1

就性能而言,它们是相同的。

  • 但即使它们不同

在这段代码中几乎可以肯定占用时间的是matcher.find(string),因为它是一个函数调用。


0
大多数人更喜欢使用下面这个,因为它包含了"&&"。
if (boolean_condition && matcher.find(string)) {
...
}

我们通常称之为“短路(或最小评估)”。这意味着仅当第一个参数无法确定表达式的值时,才会评估第二个参数(在这里是“matcher.find(string)”)。例如,如果“boolean_condition”为false,则整个条件必须为false(因为这里是逻辑AND运算符)。然后编译器不会检查第二个参数,这将导致减少代码的运行时间。

这个问题不是已经有现有答案覆盖了吗?(更重要的是——虽然在2011年这个问题可能被认为是符合主题的,但在2021年,这个问题明显过于基于个人观点而无法成为主题,这意味着如何回答中的“回答好问题”部分适用)。 - Charles Duffy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接