【问题标题】:Is a switch statement the fastest way to implement operator interpretation in Javaswitch 语句是在 Java 中实现运算符解释的最快方法吗
【发布时间】:2010-03-23 19:44:32
【问题描述】:

switch语句是Java中实现运算符解释最快的方式

   public boolean accept(final int op, int x, int val) {
     switch (op) {
        case OP_EQUAL:
          return x == val;
        case OP_BIGGER:
          return x > val;
        case OP_SMALLER:
          return x < val;
        default:
          return true;
     }
   }

在这个简单的例子中,显然是的。现在假设您有 1000 个操作员。它仍然会比类层次结构更快吗?当类层次结构在速度上比 switch 语句更有效时,是否存在阈值? (记忆中显然没有)

abstract class Op {
 abstract public boolean accept(int x, int val);
}

然后每个运算符一个类。

编辑: 对不起,我应该通过答案的外观更具体。 操作员完全未知,我使用的是 JDK 1.4。没有选择。没有枚举。没有闭包。 :( 运营商是由用户在许多许多选项中选择的。为简单起见,想象一个有 1000 个操作的 GUI 列表,当用户选择一个时,选择 switch 语句的操作码。使用类层次结构,用户将选择一个类。 我问这个问题是因为之前一定有人测试过。我不想创建 1000 个类和 1000 个伪造的操作码来测试它。如果没有人做过。我会测试它并报告结果,如果它们可能有任何意义的话。

【问题讨论】:

  • 别猜了。测量。
  • 分析器电源。但是,我会考虑将我的运算符存储在由“op”索引的方法数组中
  • enum 似乎是一个更好的选择,通过一组预定义的操作更具可读性和可维护性。

标签: java oop switch-statement


【解决方案1】:

编辑:

好的,既然你必须使用 JDK 1.4,我原来的答案是不可行(留作参考)。我猜测当您只查看 apply(which,a,b)which.apply(a,b) 调用时,switch 的速度不如基于抽象类的解决方案快。你只需要测试一下。

但是,在测试时,您可能还需要考虑启动时间、内存占用等。

原文:

public enum OPERATION {
  // ...operators+implementation, e.g.:
  GREATER_THAN { public boolean apply(int a, int b) { return a > b; } };
  public abstract boolean apply(int a, int b);
}

用法:

OPERATION x = //..however you figure out which
boolean result = x.apply(a,b);

这是在 Effective Java 中用于枚举的 案例之一。它的工作方式与switch 完全相同,只是不那么令人困惑。

【讨论】:

  • 哎呀——在 Effective Java 中,不是 Java 教程线索。
【解决方案2】:

由于 switch 语句通常在 jvm 中实现的方式,使用查找表,它可能会更快,使用少量或大量运算符。这只是猜测;要获得明确的答案,您需要在要运行的系统上对其进行基准测试。

但是,这只是一个微优化,除非分析表明它确实可以发挥作用,否则您不应该关心它。使用整数而不是特定的类(或枚举)会降低代码的可读性。包含 1000 个 case 的巨大 switch 语句是糟糕设计的标志。这将对使用运算符的代码产生影响; 可读性较差、错误较多、难以重构、...

并恢复性能,这似乎是这里的目标。在难以阅读、设计糟糕的代码中,宏观优化所需的更改变得更加困难。并且这些优化通常比像这个开关这样的微优化更重要

【讨论】:

    【解决方案3】:

    我不知道什么是最快的,我也不认为有任何保证。代码的优化很大程度上取决于编译器和运行时。

    我确实认为很难击败 switch 语句。由于 Java 对可切换类型的限制,它可以相当容易地编译为查找表,这是您可以获得的最快访问权限。

    【讨论】:

    • 好吧,我的直觉是 switch 语句是赢家,即使它是糟糕的 OOP。我仍然想知道对子类的方法解析是否会胜过它。 => subClassOfAbstractOperator.operate(x,val);
    【解决方案4】:

    使用表驱动方法,正如之前的发帖人指出的,您可以使用运算符作为数组的索引。存储在数组中的值可以是执行比较的类的实例。数组可以静态初始化,或者更好的按需初始化(延迟加载模式)。

    例如

    // Interface and classes
    interface Operator {
          boolean operate(int x, int y); 
    }
    
    class EqualsOperator implements Operator {
          boolean operate(int x, int y){
            return x==y;
          }
    }
    class NotEqualsOperator implements Operator {
          boolean operate(int x, int y){
            return x=!y;
          }
    }
    ...
    
    // Static initialization
    Operator[] operators = new Operator[n];
    operator[0] = new EqualsOperator();
    operator[1] = new NotEqualsOperator();
    ...
    
    // Switch
    public boolean accept(final int op, int x, int val) {
        operator[op].operate(x,val);
    }
    

    【讨论】:

    • 是的,谢谢。但问题是语句 operator[op].operate(x,val);比整个 switch 语句更快。
    • 简单介绍一下。但我想增益将是纳秒级,所以我不会花太多精力。
    【解决方案5】:

    如果调用方法已经决定使用哪个运算符值并调用accept(),那么最快的方法就是在同一个调用方法中进行内联比较。

    或者,使用三种方法(或策略):

    public boolean acceptGreater(int x, int val) {
       return x > val;
    }
    
    public boolean acceptLess(int x, int val) {
       return x < val;
    }
    
    public boolean acceptEquals(int x, int val) {
       return x == val;
    }
    

    【讨论】:

      【解决方案6】:

      我不会纯粹从原始性能的角度来看待这个问题,但我会将其评估为重构候选者,请参阅c2:Refactor Mercilessly。我喜欢给code resuability的答案:

      1. 如果重复一次,请复制。
      2. 如果重复两次,请重构它。

      我将添加多个 case 语句识别为重复,然后我将重构以实现 Strategy Pattern
      我会用策略后缀命名操作符类,并实现 execute 方法。

      【讨论】:

        【解决方案7】:

        我一直发现java switch 语句没有我需要的那么强大。在他的 last release lambdaj implements it 中巧妙地使用了闭包和 Hamcrest 匹配器。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-10
          • 2011-07-31
          • 1970-01-01
          • 2016-08-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多