【问题标题】:Java Enums - Switch statements vs Visitor Pattern on Enums - Performance benefits?Java Enums - Switch 语句与 Enums 上的访问者模式 - 性能优势?
【发布时间】:2010-01-29 18:14:50
【问题描述】:

我已经寻找了几天来找到这个基于性能的问题的答案。
到目前为止,在挖掘 Internet 之后,我了解到有几种方法可以在 java 中使用枚举,在 here 中有很好的记录。 好吧,作为初学者,肯定会喜欢在 switch-case 语句中使用 Enums,它可以提供清晰的代码并更好地理解代码。但另一方面,我们也有一个 访问者模式 样式的枚举实现,它确保了类型安全性和可扩展性,讨论了 here

话虽如此,回到这个问题背后的原始想法,到目前为止,我已经了解到,如果使用 Enums 正确设计了 switch-case 构造,则可以确保 case 值不是稀疏的,并且 Enum 声明是在与 switch-case 语句相同的编译单元中,java 编译器通过实现诸如 Jump Table 之类的构造对生成的字节码执行一些优化(在here 和其他地方以及Sun 的网站,我失去了链接)。现在,与多重/嵌套 if-else 构造相比,这肯定会提高性能。

我的问题是,java 如何在生成的字节码中实现基于访问者模式的枚举,与基于 switch-case 的实现相比,性能提升是多少?

考虑到我的枚举在未来可能会增长,而且我也热衷于性能,我应该更喜欢哪种类型的实现。目前,我的枚举中有一些 19 和奇数的常量。


编辑
我有一个类存储有关游戏变量的一些信息。其中一个变量是 Enum 类型。

public class GameObject {
    private Shape mShape;

    public Shape getShape() {
        return mShape;
    }
    .
    .
    .

    public static enum Shape {
        SHAPE1, SHAPE2, SHAPE3, SHAPE4, ..., SHAPE20
    };

    public void drawShape() {
        switch (this.mShape) {
        case SHAPE1:
            drawShape1();
            break;
        case SHAPE2:
            drawShape2();
            break;
        case SHAPE3:
            drawShape3();
            break;
        case SHAPE4:
            drawShape4();
            break;
        .
        .
        .
        .
        .
        case SHAPE20:
            drawShape20();
            break;
        default:
            drawUnknown();
            break;
        }
    }

}

后来我意识到将信息与逻辑分离,因此创建了另一个类并将 Enum ShapeGameObject 到这个新类 GraphicModel,我没有在其中设置 switch-case,而是实现了 特定于常量的方法时间>。是的,在此修改之后,我确实在任一类中放置了正确的导入语句。

public class GraphicModel {
    public void drawGraphicFromObject(GameObject gameObject) {
        gameObject.getShape().draw();
    }

    public static enum Shape {
        // This method is not public, instead is only called by GraphicModel
        abstract void draw();

        SHAPE1 {
            @Override
            void draw() {
                // some transformations
            }
        },
        SHAPE2 {
            @Override
            void draw() {
                // Some different transformation
            }
        },
        SHAPE3 {
            @Override
            void draw() {
                // Yet another transform
            }
        },
        .
        .
        .
        .
        UNKNOWN {
            @Override
            void draw() {
                // 
            }
        };
    }
}

后来我什至根据here建议的访问者模式实现了这一点

那么,我需要知道的是哪种实施方式更有效?当然,为了在编译时将 switch-case 转换为 jump tables,java 需要 enum 声明和 switch同一编译单元中的语句。 我应该在 GraphicModel 类中使用基于 switch 的实现还是 特定于常量的方法 实现? 相反,要清楚,性能有什么区别?

【问题讨论】:

  • 您是否有一个编写最清晰的代码示例以及说明它如何不符合规范的规范和测试结果?如果没有,你不应该沿着这些思路思考。如果是这样,请发布清楚编写的代码,我敢打赌我们可以找到一些更重要的优化。
  • 链接的 SO 问题讲述了 Enums 如何与访问者模式一起使用,而不是“枚举的访问者模式实现”。我认为最好告诉你要解决的问题是什么。一次如果你看到枚举可以用来解决你的问题,那么你可以看看它是否需要优化。
  • 这听起来像是微优化的类型,在 99.99% 的情况下没有明显的效果
  • 如果我无法以正式的方式正确表达我的理解,我深表歉意,说实话,我从来没有掌握过这样的术语,尽管我一直在使用这样的方法。而且我相信,是的,我确实在这里使用了错误的术语,就像 Dieter 在帖子中提到的那样,它被称为特定于常量的方法实现。我将发布我今天的意思的示例代码。我发帖时是凌晨 5 点。谢谢大家的指点。我同意马特的观点,但我的问题仍然存在,即使它只带来微乎其微的提升,哪个更有效?
  • 在 Effective Jave 第 2 版中,J. Bloch 提出了一个令人信服的论点,即不应经常使用 switch-case 块来迭代枚举常量。他继续发展了这样一种想法,即使用特定于常量的方法来代替 switch-case 通常可以导致更不脆弱、更易于维护的代码。将此推理应用于您的问题,我会说性能问题可能并不重要(您必须进行分析才能看到),但是特定于常量的方法模式的软件工程优势有利于它们的使用而不是 switch-case .

标签: java enums performance switch-statement visitor-pattern


【解决方案1】:

一般来说,如果在 switch 语句中使用枚举 (1),则枚举的性能与 int 常量相当。

也许您应该考虑类似于常量特定方法实现的东西?例如

public enum Mode {

  ON { Color color() {return Color.GREEN;}},
  OFF { Color color() {return Color.RED;}},
  STANDBY { Color color() {return Color.YELLOW;}},
  DEFAULT { Color color() {return Color.BLACK;}};

  abstract Color color();

}//enum Mode

然后使用

getMode().color();

而不是 switch 语句?!

但是,我认为对于“仅获取颜色的情况”,可能根本不需要方法。

总的来说,我强烈推荐您使用Effective Java 来存放您的书架。第 6 章将讨论枚举和注解

【讨论】:

  • 感谢您的建议,但这正是我第二次所做的。我也去看看这本书。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-18
  • 1970-01-01
  • 2011-06-07
  • 2010-10-18
相关资源
最近更新 更多