Какое количество членов вы можете иметь в перечислении java? - PullRequest
6 голосов
/ 01 декабря 2009

Предположим, у вас есть гипотетическое перечисление в java, подобное этому (чисто для демонстрации, это не тот код, который я серьезно ожидаю использовать):

enum Example{
    FIRST,
    SECOND,
    THIRD,
    ...
    LAST;
}

Какое максимальное число членов может быть в этом перечислении до того, как компилятор остановит вас?

Во-вторых, есть ли разница в производительности во время выполнения, когда ваш код ссылается на перечисление, скажем, 10 членов, а не 100 или 1000 (кроме как очевидные накладные расходы памяти, необходимые для хранения большого класса)?

Ответы [ 5 ]

13 голосов
/ 01 декабря 2009

Сама спецификация языка не имеет ограничений. Тем не менее, существует множество ограничений, что у classfile есть ограничение на число перечислений, причем верхняя граница составляет 65 536 (2 ^ 16) перечислений:

Количество полей JVMS 4.1 указывает, что ClassFile может иметь до 65 536 (2 ^ 16) полей. Перечисления хранятся в файле класса как статическое поле, поэтому максимальное количество значений перечисления и полей перечисления составляет 65 536.

Постоянный пул JVMS также указывает, что пул констант может иметь до 65 536. Пулы констант хранят все строковые литералы, литералы типов, супертипы, типы суперинтерфейсов, сигнатуры методов, имена методов, И имена значений перечислений. Таким образом, должно быть меньше 2 ^ 16 значений перечисления, поскольку строки имен должны совместно использовать этот предел константного пула.

Инициализация статического метода Максимальный предел для метода составляет 65 535 байт (в байт-коде). Поэтому статический инициализатор для Enum должен быть меньше 64 КБ. Хотя компилятор может разделить его на разные методы (см. Идентификатор ошибки: 4262078 ) для распределения инициализаций на небольшие блоки, компилятор в настоящее время не делает этого.

Короче говоря, нет простого ответа, и ответ зависит не только от количества значений перечисления, но также от количества методов, интерфейсов и полей, которые имеют перечисления!

10 голосов
/ 01 декабря 2009

Лучший способ узнать ответ на этот вопрос - это попробовать. Начните с небольшого скрипта Python для генерации файлов Java:

n = input()
print "class A{public static void main(String[] a){}enum B{"
print ','.join("C%d" % x for x in range(n))
print '}}'

Теперь попробуйте с 1,10,100,1000 ... работает нормально, потом БАМ:

A.java: 2: слишком большой код С0, С1, С2, С3, С4, С5, С6, С7, С8, С9, С10, С11, С12, С13, С14, С15, С16, С17, С18, С19, С20, С21, С22, ...

Похоже, я достиг своего рода внутреннего предела. Не уверен, является ли это документированным лимитом, зависит ли он от конкретной версии моего компилятора или какой-то системный лимит. Но для меня ограничение было около 3000 и, похоже, связано с размером исходного кода. Может быть, вы могли бы написать свой собственный компилятор, чтобы обойти это ограничение.

1 голос
/ 01 декабря 2009

Максимальное количество значений перечисления, я думаю, будет чуть меньше 65536 максимального количества полей / константных записей пула в классе. (Как я упоминал в комментарии выше, фактические значения не должны занимать постоянные записи пула: они могут быть «встроены» в байт-код, но имена будут.)

Что касается второго вопроса, то здесь нет прямой разницы в производительности, но вполне возможно, что будут небольшие косвенные различия в производительности, отчасти из-за размера файла класса, как вы говорите. Следует также помнить, что при использовании наборов перечислений существуют оптимизированные версии некоторых классов для случаев, когда все значения перечислений соответствуют определенному диапазону (как я помню, байт). Так что да, может быть небольшая разница. Хотя я не стану параноиком.

0 голосов
/ 01 декабря 2009

Это расширение комментариев к исходному вопросу.

Есть много проблем с большим количеством перечислений.

Основная причина в том, что когда у вас много данных, они имеют тенденцию меняться, или, если нет, вы часто хотите добавлять новые элементы. Существуют исключения для подобных преобразований модулей, которые никогда не изменятся, но по большей части вы хотите считать такие данные из файла в коллекцию классов, а не в перечисление.

Добавление новых элементов проблематично, поскольку, поскольку это перечисление, вам нужно физически изменить код, если вы ВСЕГДА не используете перечисления как коллекцию, и если вы ВСЕГДА используете их как коллекцию, зачем вообще делать их перечислениями?

Случай, когда ваши данные не меняются - например, «единицы преобразования», когда вы конвертируете футы, дюймы и т. Д. Вы МОЖЕТЕ сделать это как перечисления, и их будет много, но кодируя их как перечисления вы теряете способность управлять вашей программой. Например, пользователь может выбрать из выпадающего списка, заполненного вашими «Единицами», но опять же, это не использование «ENUM», он использует его как коллекцию.

Другой проблемой будет повторение ссылок на ваше перечисление. Вы почти наверняка будете иметь что-то очень повторяющееся:

if(userSelectedCard() == cards.HEARTS)
    graphic=loadFile("Heart.jpg");
if(userSelectedCard() == cards.SPADES)
    graphic=loadFile("Spade.jpg");

Что является неправильным (если вы можете щуриться туда, где вы не можете прочитать буквы и увидеть этот тип шаблона в своем коде, вы ЗНАЕТЕ, что делаете это неправильно).

Если бы карты хранились в коллекции карт, было бы проще просто использовать:

graphic=cards.getGraphicFor(userSelectedCard());

Я не говорю, что это также нельзя сделать с помощью enum, но я говорю, что не вижу, как вы могли бы использовать их как перечисления, не имея какого-то неприятного блока кода, подобного тому, который я опубликовал выше.

Я также не говорю, что нет случаев для перечислений - их много, но когда вы получаете больше, чем несколько (7 было хорошим числом), вам, вероятно, лучше с некоторыми другими структура.

Полагаю, исключение составляет случай, когда вы моделируете реальные вещи, которые имеют столько типов, и каждый из них должен быть адресован по-разному, но даже тогда вам, вероятно, лучше использовать файл данных, чтобы связать имя с каким-то кодом. запустите и сохраните их в хэше, чтобы вы могли вызывать их с помощью следующего кода: hash.get (nameString) .executeCode (). Таким образом, опять же, ваша «nameString» - это данные, а не жестко закодированные, что позволяет проводить рефакторинг в другом месте.

Если вы привыкли к жестокому факторингу своего кода, как это, вы можете уменьшить размер многих программ на 50% и более.

0 голосов
/ 01 декабря 2009

Если вам нужно спросить, вы, вероятно, делаете что-то не так. Фактический предел, вероятно, довольно высок, но я думаю, что перечисление с более чем 10 значениями будет весьма подозрительным. Разбейте это на связанные коллекции, или иерархию типов, или что-то еще.

...