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

发布于 2024-08-25 22:55:10 字数 515 浏览 5 评论 0原文

我深入了解了 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 ) );

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

慕烟庭风 2024-09-01 22:55:10

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

我已经测试了 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( ) )

but

Iterables.filter(
    EnumSet.allOf( MyEnum.class ),
    new Predicate< MyEnum >( ) {...}
)

并且仅使用 Arrays.asList( MyEnum.values( ) ) ,其中 java.util.List 是绝对必需的。

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.

丶情人眼里出诗心の 2024-09-01 22:55:10

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

恕我直言:这两个选项都执行得很好,因为它们都创建对象。第一种情况是一个,第二种情况是三个。出于性能原因,您可以构造一个常量来保存所有值。

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.

灯下孤影 2024-09-01 22:55:10

无论如何

,它们都通过反射来调用枚举类型的 values() 方法。

There is also Class.getEnumConstants()

under the hood they all call values() methods of enum types anyway, through reflection.

陌路黄昏 2024-09-01 22:55:10

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

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

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.

昔日梦未散 2024-09-01 22:55:10

我并不是经历了整个实现,但在我看来,EnumSet.allOf() 基本上使用与 .values() 相同的基础设施。所以我希望 EnumSet.allOf() 需要一些(可能可以忽略不计)额外的步骤(请参阅 https://bugs.java.com/bugdatabase/view_bug?bug_id=6276988)。

我似乎很清楚 foreach 的预期用途是 for(MyEnum val : MyEnum.values()) 为什么要采取不同的做法?你只会让维护程序员感到困惑。

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

无论如何,彼得·劳瑞是对的。不要担心这个的性能..它足够快,并且很可能存在数百万个其他瓶颈,使微小的理论性能差异完全无关(不过不要看到他的“对象创建”点。对我来说,第一个)示例似乎 100% OK)。

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 https://bugs.java.com/bugdatabase/view_bug?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).

故事与诗 2024-09-01 22:55:10

EnumSet 并不是为了迭代其值而构建的。相反,它的实现理念是有效(或相当有效)地表示 BitMap 或 BitMask。 关于 EnumSet 的 javadoc 还指出:

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

由于只有一个位可以表示某个 Enum 值,因此它也是作为 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));
  }
}

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));
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文