如何在Java中重构一大块if语句?

vd2z7a6w  于 2023-01-24  发布在  Java
关注(0)|答案(4)|浏览(163)

我最近使用JVisualVM分析了一些代码,发现有一个特定的方法占用了大量的执行时间,这是因为它经常被调用,而且执行速度很慢。该方法由一大块if语句组成,如下所示:(实际方法中大约有30个)

EcState c = candidate;

    if (waypoints.size() > 0)
    {
        EcState state = defaultDestination();
        for (EcState s : waypoints)
        {
            state.union(s);
        }
        state.union(this);
        return state.isSatisfied(candidate);
    }

    if (c.var1 < var1)
        return false;
    if (c.var2 < var2)
        return false;
    if (c.var3 < var3)
        return false;
    if (c.var4 < var4)
        return false;
    if ((!c.var5) & var5)
        return false;
    if ((!c.var6) & var6)
        return false;
    if ((!c.var7) & var7)
        return false;
    if ((!c.var8) & var8)
        return false;
    if ((!c.var9) & var9)
        return false;

    return true;

有没有更好的方法来编写这些if语句,或者我应该从其他地方寻找提高效率的方法?
编辑:该程序使用进化科学来开发达到给定结果的路径。具体来说,为星际争霸2构建订单。这种方法检查特定的进化是否满足给定结果的条件。

4bbkushb

4bbkushb1#

首先,你使用的是&而不是&&,所以你没有利用short circuit evaluation。也就是说,&运算符要求&两边的条件都被求值。如果你真的在做一个按位AND运算,那么这就不适用了,如果不是,请看下面。
假设在不满足条件时返回true,则可以如下重写(我将&更改为&&)。

return 
       !(c.var1 < var1 ||
       c.var2 < var2 ||
       c.var3 < var3 ||
       c.var4 < var4 ||
       ((!c.var5) && var5) ||
       ((!c.var6) && var6) ||
       ((!c.var7) && var7) ||
       ((!c.var8) && var8) ||
       ((!c.var9) && var9));

第二,你想把最有可能为真的条件移到表达式链的顶端,这样可以节省对剩余表达式的求值,例如if (c1.var4 < var4)有99%的可能性为真,你可以把它移到顶端。
除此之外,除非这些条件遇到数据库或类似的情况,否则您会在此方法中花费大量的时间,这似乎有点奇怪。

6ljaweal

6ljaweal2#

首先,尝试将if语句序列重写为一个语句(根据@dcp的答案)。
如果这没有太大区别,那么瓶颈可能是waypoints代码。

  • 您正在使用waypoints.size()开销较大的某个集合类型。
  • waypoints.size()是一个很大的数
  • defaultDestination()价格昂贵
  • state.union(...)价格昂贵
  • state.isSatisfied(...)价格昂贵

一种快速而简便的调查方法是将所有代码移到一个单独的方法中,然后查看分析器是否告诉您这是一个瓶颈。
如果这不是问题所在,那么您的问题就是难以解决的,唯一的解决方法就是找到一些聪明的方法来避免进行如此多的测试。

  • 如果存在可能更快返回false的顺序,则重新排列测试顺序可能会有所帮助。
  • 如果thisc很可能是同一个对象,那么对this == c进行初始测试可能会有所帮助。
  • 如果重复比较所有的EcState对象,并且它们是不可变的,那么可以实现hashCode来缓存其返回值,并使用hashCode来加快相等性测试(这是一个很长的机会......很多事情必须是“正确的”才能有所帮助)。
  • 也许您可以使用hashCode equality作为equality的代理...
dbf7pr2w

dbf7pr2w3#

和往常一样,最好的方法是自己测量。您可以通过调用System.nanotime()来检测此代码,以获得非常细粒度的持续时间。获得开始时间,然后计算方法的各个大块实际花费的时间。选择最慢的块,然后在其中放入更多的nanotime()调用。让我们知道您的发现。这对阅读你的问题的其他人会有帮助。
所以这是我的底限猜猜...
优化if语句几乎没有什么可衡量的效果:这些比较都相当快。
我们假设问题在这里:

if (waypoints.size() > 0)
{
    EcState state = defaultDestination();
    for (EcState s : waypoints)
    {
        state.union(s);
    }
    state.union(this);
    return state.isSatisfied(candidate);
}

我猜waypoints是一个List,并且你没有覆盖size()方法,在这个例子中,List.size()只是访问一个示例变量,所以不要担心if语句。
for语句迭代List元素的速度非常快,所以for语句本身并不是它,尽管问题很可能出在它执行的代码上,赋值和返回不花时间。
这留下了以下潜在热点:

  • defaultDestination()的一次调用。
  • 所有打给EcState.union()的电话。
  • EcState.isSatisfied()的一次调用。

我敢打赌,您的热点位于union()中,特别是因为它正在构建某种越来越大的航点集合。
首先使用nanetime()进行测量。

xn1cxnb4

xn1cxnb44#

你不会找到太多的方法来真正加快速度,两个主要的方法是利用短路求值,如前所述,通过将&切换到&&,并确保条件的顺序是有效的,例如,如果有一个条件丢弃了90%的可能性,则将该条件放在方法的第一位。

相关问题