Перечисление.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)); } }