Перечисление.values () vs EnumSet.все(). Какой из них предпочтительнее?
Я искал под капотом EnumSet.allOf
и это выглядит очень эффективно, особенно для перечислений с менее 64 значений.
в основном все наборы разделяют один массив всех возможных значений перечисления, и единственная другая часть информации-это битовая маска, которая в случае allOf
устанавливается одним махом.
С другой стороны перечисление.values () кажется немного черной магией. Кроме того, она возвращает массив, а не коллекцию, поэтому во многих случаях он должен быть украшен Матрицы.asList () будет использоваться в любом месте, которое ожидает сбор.
так,EnumSet.allOf
быть предпочтительнее Enum.values
?
более конкретно, какая форма for
итератор должен использоваться:
for ( final MyEnum val: MyEnum.values( ) );
или
for ( final MyEnum val: EnumSet.allOf( MyEnum.class ) );
6 ответов:
поскольку я не получил ответа на свой вопрос, какой из них более эффективен, я решил провести собственное тестирование.
Я проверил итерацию над
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.утиль.Коллекция, чтобы передать вокруг,
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
это абсолютно необходимо.
вы должны использовать самый простой и понятный для вас подход. Производительность не должна рассматриваться в большинстве ситуаций.
IMHO: ни один из вариантов не работает очень хорошо, поскольку они оба создают объекты. В первом случае и три во втором. Вы можете построить константу, которая содержит все значения по соображениям производительности.
появилась
Class.getEnumConstants()
под капотом они все называют
values()
методы перечисления типов в любом случае,через отражение.
The
values()
метод более ясен и эффективен, если вы просто хотите перебирать все возможные значения перечисления. Значения кэшируются классом (см.Class.getEnumConstants()
)Если вам нужно подмножество значений, вы должны использовать
EnumSet
. Начинаются сallOf()
илиnoneOf()
и добавить или удалить значения или использовать простоof()
как вам нужно.
Не то, чтобы я прошел всю реализацию, но мне кажется, что EnumSet.allOf () в основном использует ту же инфраструктуру, что и .значения.)( Так что я ожидал бы EnumSet.allOf () требует некоторых (вероятно, незначительных) дополнительных шагов (см. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6276988).
Мне кажется ясным, что предполагаемое использование foreach
for(MyEnum val : MyEnum.values())
Почему это по-другому? Вы только запутаете программиста обслуживания.I то есть, если вам нужна коллекция, вы должны ее получить. Если вы хотите использовать foreach, массивы достаточно хороши. Я бы даже предпочел массивы при нажатии! Зачем обертывать что-либо с чем-либо, если то, что вы получили (массив) достаточно хорошо? Простые вещи, как правило, быстрее.
в любом случае, Питер Lawrey прав. Не беспокойтесь о производительности этого.. Это достаточно быстро, и есть вероятность, что есть миллион других узких мест, которые делают эту крошечную теоретическую разницу в производительности совершенно неуместной (Однако не вижу его точки "создания объекта". Мне первый пример кажется 100% ОК).
EnumSet
не строится с намерением перебирать его значения. Скорее он реализован с идеей для него, чтобы представлять растровое изображение или битовую маску эффективно (или достаточно эффективно). Элемент javadoc onEnumSet
также указано:перечисления представлены внутренне в виде битовых векторов. Это представление весьма компактно и эффективно. Пространственно-временные характеристики этого класса должны быть достаточно хорошими, чтобы позволить его использование в качестве высококачественного, безопасной альтернативой традиционным инт "на основе битовых флагов."Даже массовые операции (такие как containsAll и retainAll) должны выполняться очень быстро, если их аргумент также является набором перечислений.
поскольку только один бит может представлять определенное значение перечисления, он также реализован как
Set
, а неList
.теперь, вероятно, также верно, что вы можете выполнить то же самое и быстрее, используя битовые маски C-стиля (x^2), однако он предлагает более интуитивное кодирование стиль и тип безопасного использования с использованием перечислений, и он легко расширяется за пределы размера what an
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)); } }