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安全使用,并且它可以轻松扩展intlong可以包含的大小。

因此,您可以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)); } }