Java 枚举的性能
我正在考虑使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
发现并改编了枚举和 ResourceBundle 的完美组合:
Found and adapted a nice mix of enums and ResourceBundle:
您最好使用 java.util.ResourceBundle 类。它正是为了解决这个问题而设计的。
回答您的问题:
You're probably better off using the
java.util.ResourceBundle
class. It is designed to solve exactly this problem.To answer your questions:
我讨厌劫持主题,但依赖 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.
尽管java使用ResourceBundle提供了i18n支持,但我不认为为此目的使用枚举的想法那么糟糕。我相信这两种方法可以合并。您可以创建包含所有文本标识符的
枚举文本
。您可以为每种支持的语言创建资源包,并在此包中使用相同的标识符。然后在枚举中实现
getText()
方法,如下所示:因此,您不必关心每种语言的文本初始化。标准机制关心这一点。
现在您可以在代码中使用枚举并享受捆绑包的所有功能。您还可以创建单元测试来验证所有枚举成员在捆绑包中是否具有适当的行,反之亦然,以避免捆绑包中出现垃圾。
我可能会在下一个项目中使用这种方法。谢谢你的主意!
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: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!
感谢您向我展示了我以前从未见过的编译器错误。编译生成的源文件时:
eclipse 说
使用仅 2000 个常量的相同测试可以完美编译。
当然,如果您有那么多常量,最好将它们组织到多个源文件中。
是的,为每个枚举常量分配一个(且仅一个)对象。如果有 2000 个常量,内存就高达 16KB :-)(在 Sun 的 32 位 VM 上,其他 VM 可能略有不同)
每个枚举常量都是一个对象,并且每个常量都有一个字段
text
。该字段不是最终的,因此不受内联的影响。是的,现场访问是恒定时间的。然而,一般来说,枚举中具有可变状态是很奇怪的。不过,这是可能的。
好的方法包括:
委托给 ResourceBundle,如 AlexR 所示。缺点:需要手动管理资源文件。如果您这样做,我建议使用 UnitTest 来检测错误输入/丢失/多余的资源密钥,甚至使用命令行实用程序将丢失的密钥附加到资源文件中,这样您就不必(错误)输入它们。
如果只支持几种语言,也可以将所有语言存储在 enum 中:
缺点:不需要外行编辑(需要编译器),并且源文件编码最好支持目标语言中的所有特殊字符(unicode 转义很尴尬) ...)。此外,如果您有超过 3 种或 4 种语言,源文件会变得混乱。
优点:添加/删除文本轻而易举,编译器会捕获文本名称中的所有拼写错误,并且“资源文件”始终保持一致。
无论哪种方式,您都应该使用
MessageFormat
,正如 R.Bemrose 在他的答案中链接到的教程所解释的那样。最后,在使用 Enum 时,您可能会发现 value() 方法很方便:
Kudos for showing me a compiler error I have never seen before. When compiling the source file generated by:
eclipse says
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:
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:
我同意枚举最适合 I18n 的键而不是它们翻译成的字符串。
然而,对于您的具体问题,您应该使用构造函数而不是设置器。恕我直言,事实上,在 90% 以上的情况下,您应该使用构造函数,其中值是在构造时设置的并且未更改,而不是使用 setter。
就创建枚举的性能而言,您不应该为游戏担心它,应该首先考虑清晰度和灵活性。 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.
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.