java 枚举中可以拥有的成员数量有多少限制?
假设您在java中有一个像这样的假设枚举(纯粹出于演示目的,这不是我认真期望使用的代码):
enum Example{
FIRST,
SECOND,
THIRD,
...
LAST;
}
在编译器阻止您之前,您在该枚举中可以拥有的最大成员数是多少?
其次,当您的代码引用具有 10 个成员而不是 100 或 1,000 个成员的枚举时,运行时是否存在任何性能差异(除了存储大型类所需的明显内存开销之外)?
Assuming you have a hypothetical enum in java like this (purely for demonstration purposes, this isn't code i'm seriously expecting to use):
enum Example{
FIRST,
SECOND,
THIRD,
...
LAST;
}
What's the maximum number of members you could have inside that enum before the compiler stops you?
Secondly, is there any performance difference at runtime when your code is referencing an enum with say, 10 members as opposed to 100 or 1,000 (other than just the obvious memory overhead required to store the large class)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
语言规范本身没有限制。然而,类文件对枚举数量有很多限制,上限约为 65,536 (2^16) 个枚举:
字段数量
JVMS 4.1 指定 < code>ClassFile 最多可以有 65,536 (2^16) 个字段。枚举作为静态字段存储在类文件中,因此枚举值和枚举成员字段的最大数量为 65,536。
常量池
JVMS 还规定常量池最多可以有 65,536 个。常量池存储所有字符串文字、类型文字、超类型、超接口类型、方法签名、方法名称、和枚举值名称。因此,枚举值的数量必须少于 2^16,因为名称字符串需要共享常量池限制。
静态方法初始化
方法的最大限制为 65,535 字节(以字节码表示)。因此 Enum 的静态初始值设定项必须小于 64Kb。虽然编译器可能会将其拆分为不同的方法(请参阅Bug ID:4262078 )将初始化分布到小块中,编译器当前不这样做。
长话短说,没有简单的答案,答案不仅取决于枚举值的数量,还取决于枚举具有的方法、接口和字段的数量!
The language specification itself doesn't have a limit. Yet, there are many limitations that classfile has that bound the number of enums, with the upper bound being aruond 65,536 (2^16) enums:
Number of Fields
The JVMS 4.1 specifies that
ClassFile
may have up to 65,536 (2^16) fields. Enums get stored in the classfile as static field, so the maximum number of enum values and enum member fields is 65,536.Constant Pool
The JVMS also specifies that the Constant Pool may have up to 65,536. Constant Pools store all String literals, type literals, supertype, super interfaces types, method signatures, method names, AND enum value names. So there must be fewer than 2^16 enum values, since the names strings need to share that Constant Pool limit.
Static Method Initialization
The maximum limit for a method is 65,535 bytes (in bytecode). So the static initializer for the Enum has to be smaller than 64Kb. While the compiler may split it into different methods (Look at Bug ID: 4262078) to distribute the initializations into small blocks, the compiler doesn't do that currently.
Long story short, there is no easy answer, and the answer depends not only on the number of enum values there are, but also the number of methods, interfaces, and fields the enums have!
找出此类问题答案的最佳方法就是尝试一下。从一个小的 Python 脚本开始生成 Java 文件:
现在尝试使用 1,10,100,1000... 工作正常,然后 BAM:
A.java:2: 代码太大
C0,C1,C2,C3,C4,C5,C6,C7,C8,C9,C10,C11,C12,C13,C14,C15,C16,C17,C18,C19,C20,C21,C22,...
似乎就像我达到了某种内部极限一样。不确定它是否是记录的限制,是否取决于我的编译器的特定版本,或者是否是某些系统相关的限制。但对我来说,限制是 3000 左右,并且似乎与源代码大小有关。也许您可以编写自己的编译器来绕过此限制。
The best way to find out the answer to this type of question is to try it. Start with a little Python script to generate the Java files:
Now try with 1,10,100,1000... works fine, then BAM:
A.java:2: code too large
C0,C1,C2,C3,C4,C5,C6,C7,C8,C9,C10,C11,C12,C13,C14,C15,C16,C17,C18,C19,C20,C21,C22,...
Seems like I hit some sort of internal limit. Not sure if it's a documented limit, if it's dependent on the specific version of my compiler, or if its some system dependant limit. But for me the limit was around 3000 and appears to be related to the source code size. Maybe you could write your own compiler to bypass this limit.
我认为枚举值的最大数量正好低于类中字段/常量池条目的最大数量 65536。 (正如我在上面的评论中提到的,实际值不应该占用常量池条目:它们可以“内联”到字节码中,但名称会。)
就第二个问题而言,没有直接的性能差异,但可以想象,会有小的间接性能差异,部分原因是您所说的类文件大小。另一件要记住的事情是,当您使用枚举集合时,当所有枚举值都在某个范围内(我记得是一个字节)时,某些类会有优化版本。所以是的,可能会有一点差异。不过,我不会变得偏执。
The maximum number of enum values will I think be just under the 65536 maximum number of fields/constant pool entries in the class. (As I mentioned in a comment above, the actual values shouldn't take up constant pool entries: they can be "inlined" into the bytecode, but the names will.)
As far as the second question is concerned, there's no direct performance difference, but it's conceivable that there'll be small indirect performance differences, partly because of the class file size as you say. Another thing to bear in mind is that when you use enum collections, there are optimised versions of some of the classes for when all of the enum values fit within a certain range (a byte, as I recall). So yes, there could be a small difference. I woudln't get paranoid, though.
这是对原始问题的评论的延伸。
拥有大量枚举会带来很多问题。
主要原因是,当您拥有大量数据时,它往往会发生变化,或者如果没有,您经常想要添加新项目。对此有一些例外,例如永远不会改变的单位转换,但在大多数情况下,您希望将这样的数据从文件读取到类集合而不是枚举中。
添加新项目是有问题的,因为由于它是一个枚举,因此您需要物理修改代码,除非您始终将枚举用作集合,并且如果您始终将它们用作集合,那么为什么要使它们成为枚举呢?
您的数据不会改变的情况 - 就像您要转换英尺、英寸等的“转换单位”。您可以将其作为枚举来执行,并且会有很多枚举,但是通过将它们编码为枚举,您会丢失让数据驱动您的程序的能力。例如,用户可以从由“单位”填充的下拉列表中进行选择,但同样,这不是“ENUM”用法,而是将其用作集合。
另一个问题是对枚举的引用重复。您几乎肯定会遇到一些非常重复的内容,例如:
这是错误的(如果您可以眯着眼睛看不到字母的地方并在代码中看到这种模式,那么您就知道自己做错了)。
如果卡片存储在卡片集合中,那么使用起来会更容易:
我并不是说这也不能用枚举来完成,而是说我不知道你将如何使用这些作为枚举,没有一些像我上面发布的那样令人讨厌的代码块。
我也不是说没有枚举的情况——枚举的情况有很多,但是当你得到的数量较多时(7 是一个很好的数字),你可能会更好地使用其他结构。
我想例外的是,当您对具有多种类型的现实世界的东西进行建模时,每种类型都必须使用不同的代码进行处理,但即便如此,您最好使用数据文件将名称绑定到某些代码以运行和存储它们位于哈希中,因此您可以使用以下代码调用它们:hash.get(nameString).executeCode()。这样,您的“nameString”又是数据而不是硬编码,从而允许在其他地方进行重构。
如果您养成像这样粗暴地分解代码的习惯,您可以将许多程序的大小减少 50% 或更多。
This is an extension of the comments to the original question.
There are multiple problems with having a LOT of enums.
The main reason is that when you have a lot of data it tends to change, or if not you often want to add new items. There are exemptions to this like unit conversions that would never change, but for the most part you want to read data like this from a file into a collection of classes rather than an enum.
To add new items is problematic because since it's an enum, you need to physically modify your code unless you are ALWAYS using the enums as a collection, and if you are ALWAYS using them as a collection, why make them enums at all?
The case where your data doesn't change--like "conversion units" where you are converting feet, inches, etc. You COULD do this as enums and there WOULD be a lot of them, but by coding them as enums you lose the ability to have data drive your program. For instance, a user could select from a pull-down list populated by your "Units", but again, this is not an "ENUM" usage, it's using it as a collection.
The other problem will be repetition around the references to your enum. You will almost certainly have something very repetitive like:
Which is just wrong (If you can squint to where you can't read the letters and see this kind of pattern in your code, you KNOW you are doing it wrong).
If the cards were stored in a card collection, it would be easier to just use:
I'm not saying that this can't be done with an enum as well, but I am saying that I can't see how you would use these as enums without having some nasty code-block like the one I posted above.
I'm also not saying that there aren't cases for enums--there are lots of them, but when you get more than a few (7 was a good number), you're probably better off with some other structure.
I guess the exception is when you are modeling real-world stuff that has that many types and each must be addressed with different code, but even then you are probably better off using a data file to bind a name to some code to run and storing them in a hash so you can invoke them with code like: hash.get(nameString).executeCode(). This way, again, your "nameString" is data and not hard-coded, allowing refactoring elsewhere.
If you get in the habit of brutally factoring your code like this, you can reduce many programs by 50% or more in size.
如果你一定要问,那么你可能做错了什么。实际限制可能相当高,但我认为具有超过 10 个左右值的枚举会非常可疑。将其分解为相关的集合、类型层次结构等。
If you have to ask, you're probably doing something wrong. The actual limit is probably fairly high, but an enum with more than 10 or so values would be highly suspect, I think. Break that up into related collections, or a type hierarchy, or something.