Java 枚举 - Switch 语句与枚举上的访问者模式 - 性能优势?

发布于 2024-08-19 06:52:02 字数 3279 浏览 3 评论 0 原文

我已经搜索了好几天,以找到这个基于性能的问题的答案。
到目前为止,在浏览互联网之后,我了解到有几种在 java 中使用枚举的方法,在 此处。 好吧,作为初学者,肯定希望在 switch-case 语句中使用枚举,这样可以使代码更加清晰并更好地理解。但另一方面,我们也有枚举的访问者模式样式实现,这确保了类型安全性和可扩展性,讨论此处

话虽如此,回到这个问题背后的原始想法,到目前为止,我已经了解到,如果使用枚举正确设计 switch-case 构造,这可以确保 case 值不稀疏,并且 Enum 声明位于相同的位置编译单元作为 switch-case 语句,java 编译器通过实现这样的构造作为 Jump Table (在 此处 以及 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() {
                // 
            }
        };
    }
}

后来我什至按照建议的访问者模式实现了这一点case">这里

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

I have been searching around for days to find an answer to this performance based issue.
After digging the Internet so far I have learned that there are couple of ways to use the Enums in java, well documented in here.
Well, definitely as a starter one would like use Enums in a switch-case statement, which provides clarity and better understanding of the code. But on the other hand we have a Visitor pattern style implementation of the Enums as well, which ensures type safety and extensibility, Discussed here.

Having said that, and coming back to original idea behind this question, so far I have learned that if a switch-case construct is properly designed using Enums, which ensures that case values are not sparse, and that the Enum declaration is in the same compilation unit as the switch-case statement, the java compiler performs some optimisation on the produced byte-code by implementing such construct as a Jump Table (discussed in here and elsewhere as well on Sun's website, to which I lost the link). Now, this definitely gives a boost to the performance compared to multiple/nested if-else construct.

My question is, how does java implement the visitor pattern based implementation of Enums in the resulting byte-code and what is the performance boost while compared to switch-case based implementation, if any?

And which type of implementation should I prefer, considering my Enums may grow in future and I am keen on performance as well. Currently, I have some 19 and odd constants in my Enum.


EDIT
I have a class that stores some information about variables of a game. One of the variables being an Enum type.

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

}

Later I realised of separating the information from logic, and hence created another class and moved the Enum Shape from GameObject to this new class GraphicModel, and instead of having switch-case there, I implemented constant-specific methods. And yes I did put proper import statements in either class after this modification.

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() {
                // 
            }
        };
    }
}

Later on I even implemented this based on visitor pattern as suggested here

So, what I need to know is that which way of implementation is more efficient? Definitely, for switch-case to be converted to jump tables on compilation, java requires both enum declaration and switch statements in the same compilation unit.
Should I use switch based implementation or constant-specific method implementation in my GraphicModel class?
Rather, to be clear, what's the difference in performance?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

与他有关 2024-08-26 06:52:02

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

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

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 章将讨论枚举和注释

Enums are, generally speaking, comparable in performance to int constants, if used in a switch statement (1).

Maybe you should rather consider something along the lines of constant specific method implementations? e.g.

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

And then use

getMode().color();

instead of a switch statement?!

However, I think that for a "get a color only case", there is maybe no need for methods at all.

In general, I highly recommend you Effective Java for your bookshelf. Chapter 6 would be the one that discusses Enums and Annotations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文