Enum.values()与EnumSet.allOf()。 哪一个更可取?
我查看了EnumSet.allOf
,看起来效率很高,特别是对于小于64个值的枚举。
基本上所有的集合共享所有可能枚举值的单个数组,唯一的另一条信息是一个位掩码,在allOf
情况下设置为一举。
另一方面,Enum.values()似乎有点黑魔法。 此外,它返回一个数组,而不是一个集合,所以在许多情况下,它必须用Arrays.asList()来装饰,以便在任何需要收集的地方使用。
那么, EnumSet.allOf
应该比EnumSet.allOf
更可取?
更具体地说,应该使用哪种forms的迭代器:
for ( final MyEnum val: MyEnum.values( ) );
要么
for ( final MyEnum val: EnumSet.allOf( MyEnum.class ) );
因为我没有收到关于哪一个更有效的问题的答案,所以我决定对自己的问题做一些testing。
我testing了对values()
, Arrays.asList( values() )
和EnumSet.allOf( )
。 对于不同的枚举大小,我已经重复了这些testing10,000,000次。 以下是testing结果:
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
这些是从命令行运行的testing结果。 当我从Eclipse运行这些testing时,我得到了对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
是绝对必需的。
你应该使用最简单,最清晰的方法。 在大多数情况下,性能不应该被考虑在内。
恕我直言:这两个选项都非常好,因为它们都创build对象。 一个在第一个情况下,三个在第二个。 您可以构造一个常数,其中包含所有值的性能原因。
还有Class.getEnumConstants()
在引擎盖下,他们都通过reflection调用枚举types的values()
方法。
如果您只是想遍历所有可能的枚举值,那么values()
方法就更加清晰和高效。 这些值由类caching(请参阅Class.getEnumConstants()
)
如果你需要一个值的子集,你应该使用EnumSet
。 从allOf()
或noneOf()
开始, allOf()
添加或删除值或仅使用of()
。
不是我经历了整个实现,但在我看来,EnumSet.allOf()基本上使用与.values()相同的基础结构。 所以我期望EnumSet.allOf()需要一些(可能可以忽略)额外的步骤(见http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6276988 )。
在我看来, for(MyEnum val : MyEnum.values())
,foreach的使用目的是为什么不同呢? 你只会混淆维护程序员。
我的意思是,如果你需要一个集合,你应该得到一个。 如果你想使用foreach,数组就足够好了。 如果按下,我甚至更喜欢数组! 为什么用任何东西包装什么,如果你有(arrays)是足够好的? 简单的事情通常更快。
无论如何,彼得·劳瑞是正确的。 不要担心这个事情的performance。这个速度够快,而且还有上百万个其他的瓶颈,使得这个微小的理论性能差异完全不相关(虽然没有看到他的“对象创造”例子似乎是100%确定)。
EnumSet
不是为了迭代它的值而构build的。 而是实现它的想法,它代表一个BitMap或BitMask高效(或相当有效)。 EnumSet
上的javadoc也说明:
枚举集在内部表示为位向量。 这种表示非常紧凑和高效。 这个类的空间和时间性能应该足够好,可以用作传统的基于int的“比特旗”的高质量,types安全的替代品。 即使批量操作(如containsAll和retainAll)也应该运行得非常快,如果它们的参数也是一个枚举集。
因为只有一个位可以表示某个枚举值,所以它也被实现为一个Set
而不是List
。
现在,使用C风格的位掩码(x ^ 2)也可以实现相同和更快的速度,但是它提供了更直观的编码风格和使用枚举的types安全使用,并且它可以轻松扩展int
或long
可以包含的大小。
因此,您可以testing所有位设置如下:
public class App { enum T {A,B} public static void main(String [] args) { EnumSet<T> t = EnumSet.of(TA); t.containsAll(EnumSet.allOf(T.class)); } }