Java Enum.values()vs EnumSet.allOf()。哪个更可取?

Java Enum.values()vs EnumSet.allOf()。哪个更可取?,java,enums,Java,Enums,我在引擎盖下查找了EnumSet.allOf,它看起来非常有效,尤其是对于小于64个值的枚举 基本上,所有集合共享所有可能枚举值的单个数组,唯一的其他信息是位掩码,在allOf的情况下,位掩码是一次性设置的 另一方面,Enum.values()似乎有点像黑魔法。此外,它返回一个数组,而不是集合,因此在许多情况下,它必须用Arrays.asList()修饰,才能在任何需要集合的地方使用 那么,EnumSet.allOf应该比Enum.values更可取吗 更具体地说,应该为迭代器使用哪种形式的:

我在引擎盖下查找了
EnumSet.allOf
,它看起来非常有效,尤其是对于小于64个值的枚举

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

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

那么,
EnumSet.allOf
应该比
Enum.values
更可取吗

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

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


你应该使用最简单、最清晰的方法。在大多数情况下,性能不应作为考虑因素

IMHO:这两个选项都不是很好,因为它们都创建了对象。第一种情况下有一个,第二种情况下有三个。出于性能原因,您可以构造一个保存所有值的常量。

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


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

并不是说我已经完成了整个实现,但在我看来,EnumSet.allOf()基本上使用了与.values()相同的基础结构。因此,我希望EnumSet.allOf()需要一些额外的步骤(可能可以忽略不计)(请参阅)

我似乎很清楚,foreach的预期用途是for(MyEnum val:MyEnum.values())
为什么要做不同的事情?您只会使维护程序员感到困惑

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


无论如何,彼得·劳瑞是对的。不要为这台机器的性能操心。。它的速度足够快,而且可能还有数百万个其他瓶颈使得理论上的微小性能差异变得完全不相关(但不明白他的“对象创建”观点。对我来说,第一个例子似乎100%正确)。

还有
类。getEnumConstants()


在后台,他们都调用枚举类型的
values()
方法,通过反射

因为我没有收到问题的答案,我决定自己做一些测试

我已经在
values()
Arrays.asList(values())
EnumSet.allOf()
上测试了迭代。 对于不同的枚举大小,我已经重复了10000000次这些测试。以下是测试结果:

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),
新谓词(){…}
)

并且只使用
Arrays.asList(MyEnum.values())
,其中绝对需要
java.util.List

EnumSet
不是为了迭代它的值而构建的。相反,它的实现思想是高效地(或相当高效地)表示位图或位掩码。委员会还指出:

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

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

现在,使用C风格的位掩码(x^2)也可以更快地完成同样的任务,但它提供了更直观的编码风格和使用枚举的类型安全性,并且可以轻松扩展到
int
long
所能包含的大小之外

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

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));
  }
}
公共类应用程序{
枚举T{A,B}
公共静态void main(字符串[]args){
EnumSet t=EnumSet.of(t.A);
t、 containsAll(EnumSet.allOf(t.class));
}
}

创建三个对象作为性能考虑?伙计,现在已经不是1995年了…2010年了,创建一个对象仍然不是免费的。对于大多数编程来说,创建对象并不重要,但如果性能真的重要,那么创建的对象数量仍然会产生影响。我曾参与过一个项目,其中在关键路径中创建的每个对象每年的成本超过200美元。因此,在某些上下文中,三个对象可能听起来很昂贵,特别是如果您多次这样做的话。不要在循环中实例化对象,而是在循环外部实例化它。
values()
不能被类缓存,因为它是一个数组,没有任何东西可以阻止用户更改其值。因此
Iterables.filter(
    EnumSet.allOf( MyEnum.class ),
    new Predicate< MyEnum >( ) {...}
)
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));
  }
}