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 han开发者_开发技巧d 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 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 ) );
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.
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.
There is also Class.getEnumConstants()
under the hood they all call values()
methods of enum types anyway, through reflection.
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.
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 http://bugs.sun.com/bugdatabase/view_bug.do?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).
EnumSet
is not built with the intention to iterate over it's 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 type safe 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));
}
}
精彩评论