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

时间:2010-10-25 00:05:49

标签: java refactoring if-statement decomposition

我最近使用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语句,还是应该寻找其他方法以提高效率?

编辑:该计划使用进化科学来开发特定结果的途径。具体而言,为星际争霸II建立订单。该方法检查特定进化是否满足给定结果的条件。

4 个答案:

答案 0 :(得分:4)

首先,你正在使用&amp;而不是&amp;&amp;,所以你没有利用短路评估。也就是说,&amp;运营商将要求&amp; amp;的两个条件。被评估。如果您正在进行按位AND操作,那么这将不适用,但如果没有,请参阅下文。

如果条件不满足,假设你返回true,你可以像这样重写它(我改变了&amp;到&amp;&amp;)。

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

其次,您希望尝试将最有可能真实的条件移动到表达式链的顶部,这样就可以节省对剩余表达式的评估。例如,如果c1.var4&lt; var4很可能在99%的时间都是真的,你可以把它移到顶部。

如果不这样的话,你在这个方法上花费了大量的时间似乎有点奇怪,除非这些条件会影响到数据库或类似的东西。

答案 1 :(得分:2)

首先,尝试将if语句的序列重写为一个语句(根据@ dcp的答案)。

如果这没有太大区别,那么瓶颈可能是waypoints代码。一些可能性是:

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

调查这一点的一种快速而肮脏的方法是将所有代码移动到一个单独的方法中,看看分析器是否告诉您它是一个瓶颈。

如果那不是问题那么你的问题是难以处理的,唯一的办法就是找到一些聪明的方法来避免做这么多的测试。

  • 如果订单可能会更快地返回false,则重新排列测试订单可能会有所帮助。
  • 如果thisc很可能是同一个对象,那么this == c的初始测试可能会有所帮助。
  • 如果重复比较所有EcState对象并且它们是不可变的,那么您可以实现hashCode来缓存其返回值,并使用hashCode来加速相等性测试。 (这是一个很长的镜头......很多东西必须“正确”才能提供帮助。)
  • 也许你可以使用hashCode平等作为平等的代理......

答案 2 :(得分:2)

与往常一样,最好的办法就是自己衡量。您可以通过调用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);
}

我猜测航点是一个List,你没有覆盖size()方法。在这种情况下,List.size()只是访问一个实例变量。所以不要担心if语句。

for语句很快迭代了List的元素,所以for不是它,尽管问题很可能就是它执行的代码。作业和回报不需要时间。

这留下了以下潜在的热点:

  • 打电话给defaultDestination()
  • 拨打EcState.union()的所有电话。
  • 打电话给EcState.isSatisfied()

我愿意打赌你的热点是联盟(),特别是因为它正在建立一些越来越大的航点集合。

首先用纳米()测量。

答案 3 :(得分:1)

你不会找到太多方法来实际加快速度。正如已经说过的那样,两个主要的方法是利用短路评估,将&切换为&&,并确保条件的顺序有效。例如,如果有一个条件抛弃了90%的可能性,那么将该条件放在方法的第一位。