Enum.values()vs EnumSet.allOf()。哪个更优选?

时间:2010-03-17 18:42:52

标签: java enums

我查看EnumSet.allOf的内幕,它看起来非常有效,特别是对于价值低于64的枚举。

基本上所有集合共享所有可能的枚举值的单个数组,唯一的另一条信息是位掩码,在allOf的情况下,一次性设置。

另一方面,Enum.values()似乎有点黑魔法。此外,它返回一个数组,而不是一个集合,因此在许多情况下,它必须使用Arrays.asList()进行修饰,以便在任何需要收集的地方使用。

那么,EnumSet.allOf是否应该优于Enum.values

更具体地说,应该使用哪种形式的for迭代器:

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

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

6 个答案:

答案 0 :(得分:87)

因为我没有收到关于哪一个更有效的问题的答案,所以我决定对自己进行一些测试。

我在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.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

答案 1 :(得分:12)

您应该使用最简单,最清晰的方法。在大多数情况下,性能不应该是一个考虑因素。

恕我直言:两个选项都没有表现得很好,因为它们都创造了对象。一个在第一个案例中,三个在第二个案例中。您可以构造一个常量,该常量由于性能原因而保存所有值。

答案 2 :(得分:7)

还有Class.getEnumConstants()

无论如何,他们都会调用枚举类型的values()方法,通过反思

答案 3 :(得分:4)

如果您只想迭代所有可能的枚举值,values()方法会更清晰,更高效。这些值由类缓存(请参阅Class.getEnumConstants()

如果您需要值的子集,则应使用EnumSet。从allOf()noneOf()开始,添加或删除值,或根据需要使用of()

答案 4 :(得分:2)

并非我完成了整个实现,但在我看来,EnumSet.allOf()基本上使用与.values()相同的基础结构。所以我希望EnumSet.allOf()需要一些(可能是微不足道的)额外步骤(参见http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6276988)。

我觉得foreach的预期用途是for(MyEnum val : MyEnum.values())为什么这样做会有所不同?您只会混淆维护程序员。

我的意思是,如果你需要一个系列,你应该得到一个。如果你想使用foreach,数组就足够了。如果按下我甚至更喜欢阵列!为什么用任何东西包装任何东西,如果你得到的(数组)足够好?简单的事情通常会更快。

无论如何,Peter Lawrey是对的。不要理会这个的表现。它足够快,并且有可能有数百万个其他瓶颈使得这个微小的理论性能差异完全无关紧要(不要看他的“对象创造”点。对我来说第一个例子似乎是100%OK)。

答案 5 :(得分:0)

EnumSet并非旨在迭代它的值。相反,它的实现是为了有效地(或合理地有效地)表示BitMap或BitMask。 javadoc on EnumSet还声明:

  

枚举集在内部表示为位向量。这种表现非常紧凑和高效。这个类的空间和时间性能应该足够好,以允许它作为传统的基于int的标志的高质量,类型安全的替代品。&#34;即使批量操作(例如containsAll和retainAll)如果它们的参数也是枚举集也应该非常快速地运行。

因为只有一个位可以表示某个枚举值,所以它也可以实现为Set而不是List

现在,您可以使用C风格的位掩码(x ^ 2)完成同样且更快的操作,但是它提供了更直观的编码风格和使用枚举的类型安全使用,并且它扩展了容易超出intlong可以包含的范围。

因此,您可以测试所有位的设置如下:

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