68

I looked under the hood for EnumSet.allOf() and it looks very efficient, especially for enums with less than 64 values.

Basically all sets share the single array of all possible enum values and the only other piece of information is a bitmask which in case of allOf() is set in one swoop.

On the other hand Enum.values() seems to be a bit of black magic. Moreover it returns an array, not a collection, so in many cases it must be decorated with Arrays.asList() to be usable in any place that expects a collection.

So, should EnumSet.allOf() be more preferable to Enum.values()?

More specifically, which form of for iterator should be used:

for ( final MyEnum val: MyEnum.values( ) );

or

for ( final MyEnum val: EnumSet.allOf( MyEnum.class ) );
0

7 Answers 7

99

Because I did not receive the answer to my question on which one is more efficient, I've decided to do some testing of my own.

I've tested iteration over values(), Arrays.asList( values() ) and EnumSet.allOf( ). I've repeated these tests 10,000,000 times for different enum sizes. Here are the test results:

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

These are results for tests ran from command line. When I ran these tests from Eclipse, I got overwhelming support for testValues. Basically it was smaller than EnumSet even for small enums. I believe that the performance gain comes from optimization of array iterator in for ( val : array ) loop.

On the other hand, as soon as you need a java.util.Collection to pass around, Arrays.asList( ) looses over to EnumSet.allOf, especially for small enums, which I believe will be a majority in any given codebase.

So, I would say you should use

for ( final MyEnum val: MyEnum.values( ) )

but

Iterables.filter(
    EnumSet.allOf( MyEnum.class ),
    new Predicate< MyEnum >( ) {...}
)

And only use Arrays.asList( MyEnum.values( ) ) where java.util.List is absolutely required.

Sign up to request clarification or add additional context in comments.

1 Comment

What are those numbers? Are they duration or throughput ("milliseconds" or "ops/millisecond"?)
13

You should use the approach which is simplest and clearest to you. Performance shouldn't be a consideration in most situations.

IMHO: neither option performs very well as they both create objects. One in the first case and three in the second. You could construct a constant which holds all the values for performance reasons.

3 Comments

Its 2010, and creating an object is still not free. For most programming, creating objects doesn't matter, but if performance really does matter, the number of objects you create can make still make a difference.
I have worked on one project where each object created in the critical path costs over $200 per year. So three objects can sound expensive in some contexts, especially if you do this more than once.
don't instantiate objects in a loop, instantiate it outside the loop.
8

There is also Class.getEnumConstants()

under the hood they all call values() methods of enum types anyway, through reflection.

2 Comments

How does this relate to the question I've asked?
This relates to the question, because all the others use values() under the hood.
4

The values() method is more clear and performant if you just want to iterate over all possible enum values. The values are cached by the class (see Class.getEnumConstants())

If you need a subset of values, you should use an EnumSet. Start with allOf() or noneOf() and add or remove values or use just of() as you need.

2 Comments

The values() cannot be cached by the class, because it's an array and nothing will prevent the user from changing its values. Hence, I suspect it must be a clone. EnumSet.allOf, on the other hand does use a shared value for array, hence there is definitely less memory allocation here. So, values may be more clear, but I suspect it's not more performant.
@Alexander: You are right, the array is cloned, but clone() is native. A little debugging shows me that getEnumConstants() uses values() not the other way round.
2

Not that I went through the entire implementation, but it seems to me that EnumSet.allOf() is basically using the same infrastructure as .values(). So I'd expect EnumSet.allOf() requires some (probably negligible) additional steps (see https://bugs.java.com/bugdatabase/view_bug?bug_id=6276988).

It seems clear to me that the intended use of foreach is for(MyEnum val : MyEnum.values()) why do it differently? You will only confuse the maintenance programmer.

I mean, if you need a collection, you should get one. If you want to use a foreach, arrays are good enough. I'd even prefer arrays if pressed! Why wrap anything with anything, if what you got (array) is good enough? Simple things are normally faster.

In anyways, Peter Lawrey is right. Don't bother about the performance of this.. It's fast enough, and chances are there are million other bottlenecks that render that tiny theoretical performance difference as totally irrelevant (Don't see his "object creation" point though. To me the first example seems to be 100% OK).

2 Comments

@Zwei: see my comment to Arne's post
@Alexander: OK, so they fixed the bug (see link) in JDK6? Well, I see your point then, but still I maintain the answer to your question "More specifically, which form of for iterator should be used" as "use the first example". I mean, I don't know. If you are doing some development in embedded, real time app. or something, maybe it's justifiable. But in a normal, general context? No.
1

Enum#values() returns a new array copy every time it is called. So the cheapest way to access all constants of an enum is to memoize/cache this array (or collection if you need to guarantee immutability).

So if you control all clients of your enum, the following works without copying the array (pedantic mode: it is still copied, but only exactly once when the class is loaded):

enum YourEnum {
  FIRST,
  SECOND,
  ;

  public static final YourEnum[] ALL_VALUES = values();
}

(the array is still mutable, so if you cannot guarantee that clients won't modify this array, read on).

If you cannot control the clients, you must provide an immutable collection. Iterating collections might incur slight overhead for the Iterator instance, but if you index into a List, you can avoid that cost too.

enum YourEnum {
  FIRST,
  SECOND,
  ;

  public static final List<YourEnum> ALL_VALUES = List.of(values());
}
for (final YourEnum value : YourEnum.ALL_VALUES) { // potential Iterator allocation
  // ...
}
for (int i = 0; i < YourEnum.ALL_VALUES.size(); ++i) { // guaranteed zero-allocation code
  final YourEnum value = YourEnum.ALL_VALUES[i];
  // ...
}

Usually all of that does not matter, but if you are working in a resource-constrained environment or are accessing/iterating all enum constants in a hot path of your code it might, so it's good to know your way around.


I wrote a small JMH benchmark which loops through all constants of a enum via different access methods (values(), a cached copy of the values, values copied to a list, and EnumSet.allof) with enums of different sizes (from a "tiny" enum comprising 3 constants to a "huge" enum comprising 256 constants).

Enum sizes:

  • Tiny: 3 constants
  • Small: 8 constants
  • Medium: 24 constants
  • Large: 96 constants
  • Huge: 256 constants

Here is the quick summary:

  • The cached array is consistently the fastest (20% for tiny enums and 30% for huge enums; except for "small" enums → read on). Note that for the tiny enum, there's almost no difference between the baseline and iterating the 3 enum constants.
  • EnumSet.allOf is consistently the slowest (2 to 5 times slower than the rest)
  • Allocations of the EnumSet are negligible and only become measurable from the "large" enum (96 values) and up
  • It does not matter whether you use an indexed for loop or an enhanced for loop to iterate the List containing the enum constants (this was a surprising result to me!)
  • For "large" enums and up, iterating the List is 2 times slower than iterating the array
  • For "small" enums (8 constants), the inverse could be observed: suddenly, iterating the array takes twice as long compared to the list (surprise #2! I don't know why this happens/happened)
  • Enum#values has a constant overhead of 4 * N + 16 bytes per invocation (4 bytes per enum constant plus 16 bytes for the array itself). For "huge" enums (256 constants), this allocates 1 kb of memory per invocation.
  • EnumSet for enums with more than 64 constants has an overhead of ceil(N/64.0)*8.0 + 16 bytes (1 bit per constant, rounded up to the next multiple of 8 plus 16 bytes for the backing array). Enums with up to 64 constants only require a single long to store the bitset.
  • Iterating the List seems to be slower than iterating .values(), even though values requires copying the array which the list does not (surprise #3).

Benchmarks (you can find the source at the bottom):

  • (baseline: baseline test, only "selects" the enum of the correct size)
  • values: access via Enum#values() method, enhanced for loop
  • array: access via cached array, enhanced for loop (for (var x : arr))
  • arrayIndexed: access via cached array, indexed for loop (arr[i])
  • listIterator: access via cached List, enhanced for loop
  • listIndexed: access via cached List, indexed for loop
  • enumSet: dynamic construction via EnumSet.allof, enhanced for loop

I ran the benchmarks with JMH 1.37 and Java 21 on my trusty old laptop with an i7-3740QM. I set 2 forks, 4 warmup and 4 measurement iterations, with 4 seconds each. The absolute numbers are not all that important, what matters is the relative difference.

Timing results (smaller is faster):

Benchmark                                      (enumSize)  Mode  Cnt     Score     Error   Units
EnumBenchmark.arrayIndexed                           tiny  avgt    8     4.351 ±   0.042   ns/op
EnumBenchmark.listIterator                           tiny  avgt    8     4.359 ±   0.076   ns/op
EnumBenchmark.listIndexed                            tiny  avgt    8     4.457 ±   0.281   ns/op
EnumBenchmark.baseline                               tiny  avgt    8     4.618 ±   0.054   ns/op
EnumBenchmark.enumSet                                tiny  avgt    8    12.498 ±   0.248   ns/op
EnumBenchmark.values                                 tiny  avgt    8     5.790 ±   0.101   ns/op
EnumBenchmark.array                                  tiny  avgt    8     4.471 ±   0.155   ns/op
EnumBenchmark.arrayIndexed                          small  avgt    8     8.088 ±   0.140   ns/op
EnumBenchmark.listIterator                          small  avgt    8     4.654 ±   0.069   ns/op
EnumBenchmark.listIndexed                           small  avgt    8     4.614 ±   0.061   ns/op
EnumBenchmark.baseline                              small  avgt    8     4.626 ±   0.059   ns/op
EnumBenchmark.enumSet                               small  avgt    8    22.563 ±   0.271   ns/op
EnumBenchmark.values                                small  avgt    8    11.386 ±   0.120   ns/op
EnumBenchmark.array                                 small  avgt    8     8.095 ±   0.097   ns/op
EnumBenchmark.arrayIndexed                         medium  avgt    8    16.247 ±   0.157   ns/op
EnumBenchmark.listIterator                         medium  avgt    8    27.622 ±   0.286   ns/op
EnumBenchmark.listIndexed                          medium  avgt    8    27.749 ±   0.390   ns/op
EnumBenchmark.baseline                             medium  avgt    8     4.617 ±   0.059   ns/op
EnumBenchmark.enumSet                              medium  avgt    8    58.943 ±   0.888   ns/op
EnumBenchmark.values                               medium  avgt    8    21.558 ±   2.970   ns/op
EnumBenchmark.array                                medium  avgt    8    16.269 ±   0.258   ns/op
EnumBenchmark.arrayIndexed                          large  avgt    8    47.695 ±   0.709   ns/op
EnumBenchmark.listIterator                          large  avgt    8    95.920 ±   2.063   ns/op
EnumBenchmark.listIndexed                           large  avgt    8    96.209 ±   2.917   ns/op
EnumBenchmark.baseline                              large  avgt    8     4.913 ±   0.576   ns/op
EnumBenchmark.enumSet                               large  avgt    8   221.370 ±   8.615   ns/op
EnumBenchmark.values                                large  avgt    8    58.391 ±   1.582   ns/op
EnumBenchmark.array                                 large  avgt    8    47.769 ±   0.673   ns/op
EnumBenchmark.arrayIndexed                           huge  avgt    8   112.918 ±   1.043   ns/op
EnumBenchmark.listIterator                           huge  avgt    8   224.763 ±   3.812   ns/op
EnumBenchmark.listIndexed                            huge  avgt    8   224.339 ±   3.853   ns/op
EnumBenchmark.baseline                               huge  avgt    8     4.346 ±   0.070   ns/op
EnumBenchmark.enumSet                                huge  avgt    8   571.340 ±  17.533   ns/op
EnumBenchmark.values                                 huge  avgt    8   168.228 ±   2.954   ns/op
EnumBenchmark.array                                  huge  avgt    8   112.483 ±   0.922   ns/op

Normalized memory allocations (allocations per benchmark invocation, smaller is better):

Benchmark                                      (enumSize)  Mode  Cnt     Score     Error   Units
EnumBenchmark.arrayIndexed:gc.alloc.rate.norm        tiny  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.listIterator:gc.alloc.rate.norm        tiny  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.listIndexed:gc.alloc.rate.norm         tiny  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.baseline:gc.alloc.rate.norm            tiny  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.enumSet:gc.alloc.rate.norm             tiny  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.values:gc.alloc.rate.norm              tiny  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.array:gc.alloc.rate.norm               tiny  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.arrayIndexed:gc.alloc.rate.norm       small  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.listIterator:gc.alloc.rate.norm       small  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.listIndexed:gc.alloc.rate.norm        small  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.baseline:gc.alloc.rate.norm           small  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.enumSet:gc.alloc.rate.norm            small  avgt    8    ≈ 10⁻⁴              B/op
EnumBenchmark.values:gc.alloc.rate.norm             small  avgt    8    48.000 ±   0.001    B/op
EnumBenchmark.array:gc.alloc.rate.norm              small  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.arrayIndexed:gc.alloc.rate.norm      medium  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.listIterator:gc.alloc.rate.norm      medium  avgt    8    ≈ 10⁻⁴              B/op
EnumBenchmark.listIndexed:gc.alloc.rate.norm       medium  avgt    8    ≈ 10⁻⁴              B/op
EnumBenchmark.baseline:gc.alloc.rate.norm          medium  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.enumSet:gc.alloc.rate.norm           medium  avgt    8    ≈ 10⁻⁴              B/op
EnumBenchmark.values:gc.alloc.rate.norm            medium  avgt    8   112.000 ±   0.001    B/op
EnumBenchmark.array:gc.alloc.rate.norm             medium  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.arrayIndexed:gc.alloc.rate.norm       large  avgt    8    ≈ 10⁻⁴              B/op
EnumBenchmark.listIterator:gc.alloc.rate.norm       large  avgt    8    ≈ 10⁻⁴              B/op
EnumBenchmark.listIndexed:gc.alloc.rate.norm        large  avgt    8    ≈ 10⁻⁴              B/op
EnumBenchmark.baseline:gc.alloc.rate.norm           large  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.enumSet:gc.alloc.rate.norm            large  avgt    8    32.000 ±   0.001    B/op
EnumBenchmark.values:gc.alloc.rate.norm             large  avgt    8   400.000 ±   0.001    B/op
EnumBenchmark.array:gc.alloc.rate.norm              large  avgt    8    ≈ 10⁻⁴              B/op
EnumBenchmark.arrayIndexed:gc.alloc.rate.norm        huge  avgt    8    ≈ 10⁻⁴              B/op
EnumBenchmark.listIterator:gc.alloc.rate.norm        huge  avgt    8    ≈ 10⁻³              B/op
EnumBenchmark.listIndexed:gc.alloc.rate.norm         huge  avgt    8    ≈ 10⁻³              B/op
EnumBenchmark.baseline:gc.alloc.rate.norm            huge  avgt    8    ≈ 10⁻⁵              B/op
EnumBenchmark.enumSet:gc.alloc.rate.norm             huge  avgt    8    48.001 ±   0.001    B/op
EnumBenchmark.values:gc.alloc.rate.norm              huge  avgt    8  1040.000 ±   0.001    B/op
EnumBenchmark.array:gc.alloc.rate.norm               huge  avgt    8    ≈ 10⁻⁴              B/op

JMH info:

# JMH version: 1.37
# VM version: JDK 21.0.9, OpenJDK 64-Bit Server VM, 21.0.9+10-Ubuntu-125.10
# VM invoker: /usr/lib/jvm/java-21-openjdk-amd64/bin/java
# VM options: -javaagent:...
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 4 iterations, 4 s each
# Measurement: 4 iterations, 4 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op

And the full benchmark code:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 2)
@Warmup(iterations = EnumValuesBenchmark.ITERATIONS, time = EnumValuesBenchmark.TIME, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = EnumValuesBenchmark.ITERATIONS, time = EnumValuesBenchmark.TIME, timeUnit = TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class EnumBenchmark {
    static final int ITERATIONS = 4;
    static final int TIME = 4;

    @Param({"tiny", "small", "medium", "large", "huge"})
    public String enumSize;

    private String baselineSwitch() {
        return switch (enumSize) {
            case "tiny" -> "TinyEnum";
            case "small" -> "SmallEnum";
            case "medium" -> "MediumEnum";
            case "large" -> "LargeEnum";
            case "huge" -> "HugeEnum";
            default -> throw new IllegalStateException();
        };
    }

    private Enum<?>[] enumValues() {
        return switch (enumSize) {
            case "tiny" -> TinyEnum.values();
            case "small" -> SmallEnum.values();
            case "medium" -> MediumEnum.values();
            case "large" -> LargeEnum.values();
            case "huge" -> HugeEnum.values();
            default -> throw new IllegalStateException();
        };
    }

    private Enum<?>[] enumArray() {
        return switch (enumSize) {
            case "tiny" -> TinyEnum.VALUES_ARRAY;
            case "small" -> SmallEnum.VALUES_ARRAY;
            case "medium" -> MediumEnum.VALUES_ARRAY;
            case "large" -> LargeEnum.VALUES_ARRAY;
            case "huge" -> HugeEnum.VALUES_ARRAY;
            default -> throw new IllegalStateException();
        };
    }

    private List<? extends Enum<?>> enumList() {
        return switch (enumSize) {
            case "tiny" -> TinyEnum.VALUES_LIST;
            case "small" -> SmallEnum.VALUES_LIST;
            case "medium" -> MediumEnum.VALUES_LIST;
            case "large" -> LargeEnum.VALUES_LIST;
            case "huge" -> HugeEnum.VALUES_LIST;
            default -> throw new IllegalStateException();
        };
    }

    private EnumSet<? extends Enum<?>> enumSetAllOf() {
        return switch (enumSize) {
            case "tiny" -> EnumSet.allOf(TinyEnum.class);
            case "small" -> EnumSet.allOf(SmallEnum.class);
            case "medium" -> EnumSet.allOf(MediumEnum.class);
            case "large" -> EnumSet.allOf(LargeEnum.class);
            case "huge" -> EnumSet.allOf(HugeEnum.class);
            default -> throw new IllegalStateException();
        };
    }

    // general

    @Benchmark
    public Object baseline() {
        // measure the cost of switching on the enum size
        return baselineSwitch();
    }

    @Benchmark
    public long values() {
        long result = 0L;
        for (final var value : enumValues()) {
            result ^= value.ordinal();
        }
        return result;
    }

    @Benchmark
    public long array() {
        long result = 0L;
        for (final var value : enumArray()) {
            result ^= value.ordinal();
        }
        return result;
    }

    @Benchmark
    public long arrayIndexed() {
        long result = 0L;
        final Enum<?>[] array = enumArray();
        for (int i = 0, len = array.length; i < len; i++) {
            final var value = array[i];
            result ^= value.ordinal();
        }
        return result;
    }

    @Benchmark
    public long listIterator() {
        long result = 0L;
        for (final var value : enumList()) {
            result ^= value.ordinal();
        }
        return result;
    }

    @Benchmark
    public long listIndexed() {
        long result = 0L;
        final var values = enumList();
        for (int i = 0; i < values.size(); ++i) {
            final var value = values.get(i);
            result ^= value.ordinal();
        }
        return result;
    }

    @Benchmark
    public long enumSet() {
        long result = 0L;
        for (final var value : enumSetAllOf()) {
            result ^= value.ordinal();
        }
        return result;
    }

    enum TinyEnum {
        TRUE, FALSE, FILE_NOT_FOUND,
        ;

        public static final TinyEnum[] VALUES_ARRAY = values();
        public static final List<TinyEnum> VALUES_LIST = List.of(values());
    }

    enum SmallEnum {
        NEVER, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY,
        ;

        public static final SmallEnum[] VALUES_ARRAY = values();
        public static final List<SmallEnum> VALUES_LIST = List.of(values());
    }

    enum MediumEnum {
        VALUE_1, VALUE_2, VALUE_3, VALUE_4, VALUE_5, VALUE_6, VALUE_7, VALUE_8,
        VALUE_9, VALUE_10, VALUE_11, VALUE_12, VALUE_13, VALUE_14, VALUE_15, VALUE_16,
        VALUE_17, VALUE_18, VALUE_19, VALUE_20, VALUE_21, VALUE_22, VALUE_23, VALUE_24,
        ;

        public static final MediumEnum[] VALUES_ARRAY = values();
        public static final List<MediumEnum> VALUES_LIST = List.of(values());
    }

    enum LargeEnum {
        VALUE_1, VALUE_2, VALUE_3, VALUE_4, VALUE_5, VALUE_6, VALUE_7, VALUE_8,
        VALUE_9, VALUE_10, VALUE_11, VALUE_12, VALUE_13, VALUE_14, VALUE_15, VALUE_16,
        VALUE_17, VALUE_18, VALUE_19, VALUE_20, VALUE_21, VALUE_22, VALUE_23, VALUE_24,
        VALUE_25, VALUE_26, VALUE_27, VALUE_28, VALUE_29, VALUE_30, VALUE_31, VALUE_32,
        VALUE_33, VALUE_34, VALUE_35, VALUE_36, VALUE_37, VALUE_38, VALUE_39, VALUE_40,
        VALUE_41, VALUE_42, VALUE_43, VALUE_44, VALUE_45, VALUE_46, VALUE_47, VALUE_48,
        VALUE_49, VALUE_50, VALUE_51, VALUE_52, VALUE_53, VALUE_54, VALUE_55, VALUE_56,
        VALUE_57, VALUE_58, VALUE_59, VALUE_60, VALUE_61, VALUE_62, VALUE_63, VALUE_64,
        VALUE_65, VALUE_66, VALUE_67, VALUE_68, VALUE_69, VALUE_70, VALUE_71, VALUE_72,
        VALUE_73, VALUE_74, VALUE_75, VALUE_76, VALUE_77, VALUE_78, VALUE_79, VALUE_80,
        VALUE_81, VALUE_82, VALUE_83, VALUE_84, VALUE_85, VALUE_86, VALUE_87, VALUE_88,
        VALUE_89, VALUE_90, VALUE_91, VALUE_92, VALUE_93, VALUE_94, VALUE_95, VALUE_96,
        ;

        public static final LargeEnum[] VALUES_ARRAY = values();
        public static final List<LargeEnum> VALUES_LIST = List.of(values());
    }

    enum HugeEnum {
        VALUE_1, VALUE_2, VALUE_3, VALUE_4, VALUE_5, VALUE_6, VALUE_7, VALUE_8,
        VALUE_9, VALUE_10, VALUE_11, VALUE_12, VALUE_13, VALUE_14, VALUE_15, VALUE_16,
        VALUE_17, VALUE_18, VALUE_19, VALUE_20, VALUE_21, VALUE_22, VALUE_23, VALUE_24,
        VALUE_25, VALUE_26, VALUE_27, VALUE_28, VALUE_29, VALUE_30, VALUE_31, VALUE_32,
        VALUE_33, VALUE_34, VALUE_35, VALUE_36, VALUE_37, VALUE_38, VALUE_39, VALUE_40,
        VALUE_41, VALUE_42, VALUE_43, VALUE_44, VALUE_45, VALUE_46, VALUE_47, VALUE_48,
        VALUE_49, VALUE_50, VALUE_51, VALUE_52, VALUE_53, VALUE_54, VALUE_55, VALUE_56,
        VALUE_57, VALUE_58, VALUE_59, VALUE_60, VALUE_61, VALUE_62, VALUE_63, VALUE_64,
        //
        VALUE_65, VALUE_66, VALUE_67, VALUE_68, VALUE_69, VALUE_70, VALUE_71, VALUE_72,
        VALUE_73, VALUE_74, VALUE_75, VALUE_76, VALUE_77, VALUE_78, VALUE_79, VALUE_80,
        VALUE_81, VALUE_82, VALUE_83, VALUE_84, VALUE_85, VALUE_86, VALUE_87, VALUE_88,
        VALUE_89, VALUE_90, VALUE_91, VALUE_92, VALUE_93, VALUE_94, VALUE_95, VALUE_96,
        VALUE_97, VALUE_98, VALUE_99, VALUE_100, VALUE_101, VALUE_102, VALUE_103, VALUE_104,
        VALUE_105, VALUE_106, VALUE_107, VALUE_108, VALUE_109, VALUE_110, VALUE_111, VALUE_112,
        VALUE_113, VALUE_114, VALUE_115, VALUE_116, VALUE_117, VALUE_118, VALUE_119, VALUE_120,
        VALUE_121, VALUE_122, VALUE_123, VALUE_124, VALUE_125, VALUE_126, VALUE_127, VALUE_128,
        //
        VALUE_129, VALUE_130, VALUE_131, VALUE_132, VALUE_133, VALUE_134, VALUE_135, VALUE_136,
        VALUE_137, VALUE_138, VALUE_139, VALUE_140, VALUE_141, VALUE_142, VALUE_143, VALUE_144,
        VALUE_145, VALUE_146, VALUE_147, VALUE_148, VALUE_149, VALUE_150, VALUE_151, VALUE_152,
        VALUE_153, VALUE_154, VALUE_155, VALUE_156, VALUE_157, VALUE_158, VALUE_159, VALUE_160,
        VALUE_161, VALUE_162, VALUE_163, VALUE_164, VALUE_165, VALUE_166, VALUE_167, VALUE_168,
        VALUE_169, VALUE_170, VALUE_171, VALUE_172, VALUE_173, VALUE_174, VALUE_175, VALUE_176,
        VALUE_177, VALUE_178, VALUE_179, VALUE_180, VALUE_181, VALUE_182, VALUE_183, VALUE_184,
        VALUE_185, VALUE_186, VALUE_187, VALUE_188, VALUE_189, VALUE_190, VALUE_191, VALUE_192,
        //
        VALUE_193, VALUE_194, VALUE_195, VALUE_196, VALUE_197, VALUE_198, VALUE_199, VALUE_200,
        VALUE_201, VALUE_202, VALUE_203, VALUE_204, VALUE_205, VALUE_206, VALUE_207, VALUE_208,
        VALUE_209, VALUE_210, VALUE_211, VALUE_212, VALUE_213, VALUE_214, VALUE_215, VALUE_216,
        VALUE_217, VALUE_218, VALUE_219, VALUE_220, VALUE_221, VALUE_222, VALUE_223, VALUE_224,
        VALUE_225, VALUE_226, VALUE_227, VALUE_228, VALUE_229, VALUE_230, VALUE_231, VALUE_232,
        VALUE_233, VALUE_234, VALUE_235, VALUE_236, VALUE_237, VALUE_238, VALUE_239, VALUE_240,
        VALUE_241, VALUE_242, VALUE_243, VALUE_244, VALUE_245, VALUE_246, VALUE_247, VALUE_248,
        VALUE_249, VALUE_250, VALUE_251, VALUE_252, VALUE_253, VALUE_254, VALUE_255, VALUE_256,
        ;

        public static final HugeEnum[] VALUES_ARRAY = values();
        public static final List<HugeEnum> VALUES_LIST = List.of(values());
    }
}

Maybe somebody can provide an explanation for surprise #2 and #3? They are not what I expect, so either there is something happening in the JVM that I'm not aware of or my benchmark is flawed (or both!).

Comments

0

EnumSet is not built with the intention to iterate over its values. Rather it is implemented with the idea for it to represent a BitMap or BitMask efficiently (or reasonably efficient). The javadoc on EnumSet also states:

Enum sets are represented internally as bit vectors. This representation is extremely compact and efficient. The space and time performance of this class should be good enough to allow its use as a high-quality, typesafe alternative to traditional int-based "bit flags." Even bulk operations (such as containsAll and retainAll) should run very quickly if their argument is also an enum set.

Because only one single bit can represent a certain Enum value, it is also implemented as a Set and not as a List.

Now, it is probably also true that you can accomplish the same, and faster, using C-style bit masks (x^2); however it offers a more intuitive coding style and typesafe use using enums, and it expands easily beyond the size of what an int or long can contain.

As such you can test that all bits are set as follows:

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));
  }
}

4 Comments

You got it absolutely backwards. EnumSet first and foremost is a Collection, actually a Set. Because of the properties of enums it turns out that the most efficient representation of such a set is a bit mask. Also, notice that containsAll in your example is not unique to EnumSet, it is a method from Set. But this aside, you did not answer the originally posted question, what form is more efficient when you need access to all values in the enum.
@AlexanderPogrebnyak substantiate what I have exactly backward as it is unclear to me. I never made any claims about EnumSet being anything other than a Set. Consequently it comes natural that methods like containsAll and retainAll are not unique to EnumSet, although they have a completely unique implementation. As far as answers, you seemed to have provided an excellent one with metrics. I only intended to supplement because I do not believe choice should me made based only on an iteration over the complete set of enum values.
Sometimes iterating over the complete set of enum values is the only choice you have. For example when you have to validate and assign externally passed value to enum, and that value does not clearly maps to enum name, so you cannot use Enum.valueOf.
@AlexanderPogrebnyak iterate once to build a Map, then use the map to translate your 'external value' to an enum constant. You can implement a convenience method that works similar to the built in valueOf method. Such approach makes your original question also irrelevant as building the map should happen only once as part of the static initialization of your enum.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.