Enum.values () vs EnumSet.allOf(). Какой из них предпочтительнее?
Я смотрел под капотом на EnumSet.allOf
, и он выглядит очень эффективно, особенно для перечислений с менее чем 64 значениями.
В основном все наборы совместно используют один массив из всех возможных значений enum, и единственная другая часть информации - это битмаска, которая в случае allOf
устанавливается одним махом.
С другой стороны Enum.values () кажется немного черной магией. Кроме того, он возвращает массив, а не коллекцию, поэтому во многих случаях он должен быть украшен Arrays.asList() для использования в любом месте, которое ожидает коллекцию.
Итак, если EnumSet.allOf
предпочтительнее Enum.values
?
Более конкретно, какую форму итератора for
следует использовать:
for ( final MyEnum val: MyEnum.values( ) );
или
for ( final MyEnum val: EnumSet.allOf( MyEnum.class ) );
Ответы
Ответ 1
Поскольку я не получил ответа на мой вопрос, по которому более эффективен, я решил провести собственное тестирование.
Я тестировал итерацию по values()
, Arrays.asList( values() )
и EnumSet.allOf( )
.
Я повторил эти тесты 10 000 000 раз для разных размеров перечислений. Вот результаты теста:
oneValueEnum_testValues 1.328
oneValueEnum_testList 1.687
oneValueEnum_testEnumSet 0.578
TwoValuesEnum_testValues 1.360
TwoValuesEnum_testList 1.906
TwoValuesEnum_testEnumSet 0.797
ThreeValuesEnum_testValues 1.343
ThreeValuesEnum_testList 2.141
ThreeValuesEnum_testEnumSet 1.000
FourValuesEnum_testValues 1.375
FourValuesEnum_testList 2.359
FourValuesEnum_testEnumSet 1.219
TenValuesEnum_testValues 1.453
TenValuesEnum_testList 3.531
TenValuesEnum_testEnumSet 2.485
TwentyValuesEnum_testValues 1.656
TwentyValuesEnum_testList 5.578
TwentyValuesEnum_testEnumSet 4.750
FortyValuesEnum_testValues 2.016
FortyValuesEnum_testList 9.703
FortyValuesEnum_testEnumSet 9.266
Это результаты для тестов, запущенных из командной строки. Когда я запускал эти тесты из Eclipse, я получил подавляющую поддержку testValues
. В основном он был меньше EnumSet
даже для небольших перечислений. Я считаю, что увеличение производительности происходит от оптимизации итератора массива в цикле for ( val : array )
.
С другой стороны, как только вам понадобится java.util.Collection, чтобы пройти, Arrays.asList( )
теряет до EnumSet.allOf
, особенно для небольших перечислений, которые, я считаю, будут большинством в любой заданной кодовой базе.
Итак, я бы сказал, что вы должны использовать
for ( final MyEnum val: MyEnum.values( ) )
но
Iterables.filter(
EnumSet.allOf( MyEnum.class ),
new Predicate< MyEnum >( ) {...}
)
И используйте только Arrays.asList( MyEnum.values( ) )
, где java.util.List
абсолютно необходимо.
Ответ 2
Вы должны использовать самый простой и понятный для вас подход. Производительность не должна рассматриваться в большинстве ситуаций.
IMHO: ни одна из них не работает очень хорошо, поскольку они оба создают объекты. Один в первом случае и три во втором. Вы можете построить константу, которая содержит все значения по соображениям производительности.
Ответ 3
Существует также Class.getEnumConstants()
под капотом все они все равно называют values()
методами типов перечислений, через отражение.
Ответ 4
Метод values()
более ясен и эффективен, если вы просто хотите перебрать все возможные значения enum. Значения кэшируются классом (см. Class.getEnumConstants()
)
Если вам нужно подмножество значений, вы должны использовать EnumSet
. Начните с allOf()
или noneOf()
и добавьте или удалите значения или используйте только of()
по мере необходимости.
Ответ 5
Не то, чтобы я прошел всю реализацию, но мне кажется, что EnumSet.allOf() в основном использует ту же инфраструктуру, что и .values (). Поэтому я ожидаю, что EnumSet.allOf() потребует некоторых (возможно, незначительных) дополнительных шагов (см. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6276988).
Мне кажется ясным, что предполагаемое использование foreach for(MyEnum val : MyEnum.values())
почему это происходит по-другому? Вы только смутите программиста по обслуживанию.
Я имею в виду, если вам нужна коллекция, вы должны ее получить. Если вы хотите использовать foreach, массивы достаточно хороши. Я даже предпочитаю массивы при нажатии! Зачем что-то обертывать чем угодно, если то, что вы получили (массив), достаточно хорошо? Простые вещи обычно быстрее.
В любом случае, Питер Лори прав. Не беспокойтесь о производительности этого. Это достаточно быстро, и есть вероятность, что есть миллионы других узких мест, которые делают эту крошечную теоретическую разницу в производительности совершенно неактуальной (не смей его точку создания объекта). например, на 100% нормально).
Ответ 6
EnumSet
не построен с намерением перебрать его значения. Скорее он реализован с идеей, чтобы он представлял BitMap или BitMask эффективно (или достаточно эффективно). В javadoc на EnumSet
также указано:
Множества Enum представлены внутри как битовые векторы. Это представление чрезвычайно компактно и эффективно. Пространство и время работы этого класса должны быть достаточно хорошими, чтобы позволить использовать его как высококачественную, типичную альтернативу традиционным "бит-флагам" на основе int. Даже массовые операции (такие как containsAll и keepAll) должны выполняться очень быстро, если их аргумент также является перечислением.
Поскольку только один бит может представлять определенное значение Enum, он также реализуется как Set
, а не как List
.
Теперь, вероятно, также верно, что вы можете выполнить то же самое и быстрее, используя битовые маски C-стиля (x ^ 2), однако он предлагает более интуитивно понятный стиль кодирования и безопасное использование типов с использованием перечислений, и он расширяется легко превышающий размер того, что может содержать int
или long
.
Таким образом, вы можете проверить, что все биты установлены следующим образом:
public class App {
enum T {A,B}
public static void main(String [] args) {
EnumSet<T> t = EnumSet.of(T.A);
t.containsAll(EnumSet.allOf(T.class));
}
}