Java 枚举的性能

发布于 2024-10-08 03:52:09 字数 795 浏览 1 评论 0原文

我正在考虑使用 enum 类型来管理我正在开发的 Java 游戏中的 i18n,但我很好奇在使用具有大量元素(我认为有数千个)的枚举时可能出现的性能问题。

实际上,我正在尝试类似的操作:

public enum Text {
  STRING1,
  STRING2,
  STRING3;

  public String text() {
    return text;
  }

  public String setText() {
    this.text = text;
  }
}

然后要加载它们,我只需填写字段即可:

static
{
  Text.STRING1.setText("My localized string1"); 
  Text.STRING2.setText("My localized string2"); 
  Text.STRING3.setText("My localized string3"); 
}

当然,当我必须管理多种语言时,我将从文件中加载它们。

我要问的是,

  • 是否为每个元素分配了一个对象(除了字符串之外)? (我想是的,因为枚举是用对象实现的)
  • 如何从枚举中检索正确的元素?编译时是静态的吗? (我的意思是当我在某个地方使用 Text.STRING1.text() 时)。所以它应该是恒定的复杂性,或者也许它们只是在编译阶段被替换......
  • 总的来说,这是一个好方法还是我应该期待其他东西?

谢谢

I was thinking about using enum type to manage i18n in a Java game I'm developing but I was curious about performance issues that can occur when working with enums that have lots of elements (thousands I think).

Actually I'm trying something like:

public enum Text {
  STRING1,
  STRING2,
  STRING3;

  public String text() {
    return text;
  }

  public String setText() {
    this.text = text;
  }
}

Then to load them I can just fill the fields:

static
{
  Text.STRING1.setText("My localized string1"); 
  Text.STRING2.setText("My localized string2"); 
  Text.STRING3.setText("My localized string3"); 
}

Of course when I'll have to manage many languages I'll load them from a file.

What I'm asking is

  • is an obect allocated (in addition to the string) for every element? (I guess yes, since enums are implemented with objects)
  • how is the right element retrieved from the enum? is it static at compile time? (I mean when somewhere I use Text.STRING1.text()). So it should be constant complexity or maybe they are just replaced during the compiling phase..
  • in general, is it a good approach or should I look forward something else?

Thanks

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

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

发布评论

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

评论(6

扶醉桌前 2024-10-15 03:52:10

发现并改编了枚举和 ResourceBundle 的完美组合:

public enum Text {
  YELL, SWEAR, BEG, GREET /* and more */ ;

  /** Resources for the default locale */
  private static final ResourceBundle res =
    ResourceBundle.getBundle("com.example.Messages");

  /** @return the locale-dependent message */
  public String toString() {
    return res.getString(name() + ".string");
  }
}

# File com/example/Messages.properties
# default language (english) resources
YELL.string=HEY!
SWEAR.string=§$%&
BEG.string=Pleeeeeease!
GREET.string=Hello player!

# File com/example/Messages_de.properties
# german language resources
YELL.string=HEY!
SWEAR.string=%&$§
BEG.string=Biiiiiitte!
GREET.string=Hallo Spieler!

Found and adapted a nice mix of enums and ResourceBundle:

public enum Text {
  YELL, SWEAR, BEG, GREET /* and more */ ;

  /** Resources for the default locale */
  private static final ResourceBundle res =
    ResourceBundle.getBundle("com.example.Messages");

  /** @return the locale-dependent message */
  public String toString() {
    return res.getString(name() + ".string");
  }
}

# File com/example/Messages.properties
# default language (english) resources
YELL.string=HEY!
SWEAR.string=§$%&
BEG.string=Pleeeeeease!
GREET.string=Hello player!

# File com/example/Messages_de.properties
# german language resources
YELL.string=HEY!
SWEAR.string=%&$§
BEG.string=Biiiiiitte!
GREET.string=Hallo Spieler!
不必你懂 2024-10-15 03:52:10

您最好使用 java.util.ResourceBundle 类。它正是为了解决这个问题而设计的。

回答您的问题:

  1. 是的,每个枚举值只有一个实例。
  2. 是的,查找枚举值的复杂性恒定。
  3. 并不真地。改变枚举的内容/行为有点违背了枚举的初衷。它们应该表示具有类型安全性的固定范围常量。您可以做这种事情,但这不是它们的设计目的。

You're probably better off using the java.util.ResourceBundle class. It is designed to solve exactly this problem.

To answer your questions:

  1. Yes, there is exactly one instance of each enum value.
  2. Yes, constant complexity for looking up an Enum value.
  3. Not really. Changing the content/behaviour of the enum kinda defeats the purpose of having enums in the first place. They're supposed to represent fixed-range constants with type safety. You can do this kind of thing but that's not what they were designed for.
西瓜 2024-10-15 03:52:10

我讨厌劫持主题,但依赖 i18n 枚举最终会让你陷入困境。 Java 具有适当的 i18n 支持,甚至有一个 教程为了它。

I hate to hijack to topic, but relying on enums for i18n is going to eventually paint you into a corner. Java has proper i18n support, even going so far as to have a tutorial for it.

未蓝澄海的烟 2024-10-15 03:52:10

尽管java使用ResourceBundle提供了i18n支持,但我不认为为此目的使用枚举的想法那么糟糕。我相信这两种方法可以合并。您可以创建包含所有文本标识符的枚举文本。您可以为每种支持的语言创建资源包,并在此包中使用相同的标识符。

然后在枚举中实现 getText() 方法,如下所示:

return ResourceBundle.getBundle("texts").getString(name());

因此,您不必关心每种语言的文本初始化。标准机制关心这一点。

现在您可以在代码中使用枚举并享受捆绑包的所有功能。您还可以创建单元测试来验证所有枚举成员在捆绑包中是否具有适当的行,反之亦然,以避免捆绑包中出现垃圾。

我可能会在下一个项目中使用这种方法。谢谢你的主意!

although java has i18n support using ResourceBundle I do not think that idea to use enum for this purpose is so bad. I believe that these 2 approaches can be merged. You can create enum Texts that contains all your text identifiers. You can create resource bundles for each supported language and use the same identifiers in this bundle.

Then implement getText() method in the enum as following:

return ResourceBundle.getBundle("texts").getString(name());

So, you do not have to care about the initialization of texts for each language. The standard mechanism cares about this.

Now you use in code the enums and enjoy all features of bundles. You can also create unit test that verifies that all enum members have appropriate lines in bundle and vice versa to avoid garbage in your bundles.

I will probably use this approach in my next project. Thank you for the idea!

思慕 2024-10-15 03:52:10

感谢您向我展示了我以前从未见过的编译器错误。编译生成的源文件时:

public static void main(String[] args) throws Exception {
    PrintWriter w = new PrintWriter("C:\\test.java");
    w.println("enum Test {");
    for (int i = 0; i < 3000; i++) {
        w.println("c" + i + ",");
    }
    w.println("}");
    w.close();
}

eclipse 说

静态初始化器的代码是
超过 65535 字节限制

使用仅 2000 个常量的相同测试可以完美编译。

当然,如果您有那么多常量,最好将它们组织到多个源文件中。

是的,为每个枚举常量分配一个(且仅一个)对象。如果有 2000 个常量,内存就高达 16KB :-)(在 Sun 的 32 位 VM 上,其他 VM 可能略有不同)

每个枚举常量都是一个对象,并且每个常量都有一个字段 text。该字段不是最终的,因此不受内联的影响。是的,现场访问是恒定时间的。

然而,一般来说,枚举中具有可变状态是很奇怪的。不过,这是可能的。

好的方法包括:

委托给 ResourceBundle,如 AlexR 所示。缺点:需要手动管理资源文件。如果您这样做,我建议使用 UnitTest 来检测错误输入/丢失/多余的资源密钥,甚至使用命令行实用程序将丢失的密钥附加到资源文件中,这样您就不必(错误)输入它们。

如果只支持几种语言,也可以将所有语言存储在 enum 中:

enum Message {
    Hello("Hello", "Hallo", "Salut");

    String en;
    String de;
    String fr;

    Message(String en, String de, String fr) {
        this.en = en;
        this.fr = fr;
        this.it = it;
    }

缺点:不需要外行编辑(需要编译器),并且源文件编码最好支持目标语言中的所有特殊字符(unicode 转义很尴尬) ...)。此外,如果您有超过 3 种或 4 种语言,源文件会变得混乱。

优点:添加/删除文本轻而易举,编译器会捕获文本名称中的所有拼写错误,并且“资源文件”始终保持一致。

无论哪种方式,您都应该使用 MessageFormat,正如 R.Bemrose 在他的答案中链接到的教程所解释的那样。

最后,在使用 Enum 时,您可能会发现 value() 方法很方便:

for (Text t : Text.values()) {

}

Kudos for showing me a compiler error I have never seen before. When compiling the source file generated by:

public static void main(String[] args) throws Exception {
    PrintWriter w = new PrintWriter("C:\\test.java");
    w.println("enum Test {");
    for (int i = 0; i < 3000; i++) {
        w.println("c" + i + ",");
    }
    w.println("}");
    w.close();
}

eclipse says

The code for the static initializer is
exceeding the 65535 bytes limit

Same test with a mere 2000 constants compiles flawlessly.

Of course, if you have that many constants, it would be a good idea to organize them into more than one source file.

Yes, one (and only one) object is allocated for every enum constant. With 2000 constants, that's a whopping 16KB memory :-) (on Sun's 32-bit VM, other VMs might differ a little)

Each enum constant is an object, and each of them has a field text. The field is not final, and hence not subject to inlining. Yes, field access is constant-time.

However, in general it's wierd having mutable state in an enum. It's possible, though.

Good approaches include:

Delegate to a ResourceBundle as AlexR shows. Disadvantage: You have to manually manage the resource files. If you do that, I recommend a UnitTest to detect mistyped/missing/superfluous resource keys, or even a command line utility to append the missing keys to the resource file so you don't have to (mis-)type them.

If you only support a few languages, you can alternatively store all languages in the enum:

enum Message {
    Hello("Hello", "Hallo", "Salut");

    String en;
    String de;
    String fr;

    Message(String en, String de, String fr) {
        this.en = en;
        this.fr = fr;
        this.it = it;
    }

Disadvantages: No editing by laymen (needs a compiler), and the source file encoding had better support all special characters in the target language (unicode escapes are awkward ...). Also, the source file gets cluttered if you have more than 3 or 4 languages.

Advantages: Adding/Deleting texts is a snap, and the compiler catches all typos in the name of the text, and the "resource file" is always consistent.

Either way, you should use MessageFormat as the tutorial R.Bemrose links to in his answer explains.

And finally, when working with Enums you might find the values() method handy:

for (Text t : Text.values()) {

}
地狱即天堂 2024-10-15 03:52:10

我同意枚举最适合 I18n 的键而不是它们翻译成的字符串。
然而,对于您的具体问题,您应该使用构造函数而不是设置器。恕我直言,事实上,在 90% 以上的情况下,您应该使用构造函数,其中值是在构造时设置的并且未更改,而不是使用 setter。

public enum Text {
  STRING1("String one"),
  STRING2("String two"),
  STRING3("String two");

  private final String text;
  private Text(String text) { this.text = text; }
}

就创建枚举的性能而言,您不应该为游戏担心它,应该首先考虑清晰度和灵活性。 1000 个枚举可能会使应用程序的启动时间增加 1 毫秒。 cf 从文件加载文本可能会增加 10 毫秒。

I agree that an enum is best for the keys of I18n rather than the strings they translate to.
However to your specific problem, you should a constructor rather than a setter. IMHO, In fact you should use a constructor in 90%+ of cases where a value is set on construction and not changed rather than using a setter.

public enum Text {
  STRING1("String one"),
  STRING2("String two"),
  STRING3("String two");

  private final String text;
  private Text(String text) { this.text = text; }
}

In terms of performance of creating enums, you shouldn't worry about it for a game, clarify and flexibility should be considered first. A 1000 enums might add 1 ms to the startup time of your app. c.f. Loading the text from a file is likely to add 10 ms.

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