Java 使用枚举的 8 种方式
Java 8 way to work with an enum
我想知道 Java 8 中使用枚举的所有值的最佳方法是什么。特别是当您需要获取所有值并将其添加到某个地方时,例如,假设我们有以下枚举:
public enum Letter {
A, B, C, D;
}
我当然可以做到以下几点:
for (Letter l : Letter.values()) {
foo(l);
}
但是,我还可以将以下方法添加到枚举定义中:
public static Stream<Letter> stream() {
return Arrays.stream(Letter.values());
}
然后将上面的 for 替换为:
Letter.stream().forEach(l -> foo(l));
这种方法是否可行,或者它在设计或性能上是否存在缺陷?此外,为什么枚举没有 stream() 方法?
我的猜测是枚举的大小是有限的(即大小不受语言限制但受使用限制)因此它们不需要本地 流api。当您必须操作转换并重新收集流中的元素时,流非常有用;这些不是枚举的常见用例(通常你迭代枚举值,但很少需要转换、映射和收集它们)。
如果您只需要对每个元素执行操作,也许您应该只公开一个 forEach 方法
public static void forEach(Consumer<Letter> action) {
Arrays.stream(Letter.values()).forEach(action);
}
.... //example of usage
Letter.forEach(e->System.out.println(e));
三个问题:三部分答案:
从设计的角度来看可以吗?
当然可以。没有错。如果您需要对枚举进行大量迭代,流 API 是一种干净的方法,将样板隐藏在一个小方法后面就可以了。尽管我认为 OldCumudgeon’s version 更好。
从性能的角度来看可以吗?
这很可能无关紧要。大多数时候,枚举并没有那么大。因此,在 99.9% 的情况下,一种方法或另一种方法的开销可能无关紧要。
当然还有0.1%的地方。在那种情况下:使用您的真实数据和消费者进行正确衡量。
如果我不得不打赌,我希望 for each
循环更快,因为它更直接地映射到内存模型,但在谈论性能时不要猜测,也不要调整在实际需要调整之前。以正确的方式编写代码,其次是易于阅读,然后才担心代码风格的性能。
为什么枚举没有正确地集成到流中API?
如果您将 Java 的 Stream API 与许多其他语言中的等效项进行比较,它似乎非常有限。缺少各种部分(例如,可重用的流和作为流的可选)。另一方面,实现 Stream API 对于 API 来说无疑是一个巨大的变化。由于某种原因,它被推迟了多次。所以我猜 Oracle 希望将更改限制在最重要的用例中。无论如何,枚举的使用并不多。当然,每个项目都有几个,但与列表和其他集合的数量相比,它们算不了什么。即使你有一个枚举,在很多情况下你也不会迭代它。另一方面,列表和集合可能几乎每次都被迭代。我认为这些是 Enums 没有得到自己的 Stream 世界适配器的原因。我们将看看在未来的版本中是否会添加更多内容。在那之前你总是可以使用 Arrays.stream
.
我认为获得枚举常量 Stream
的最短代码是 Stream.of(Letter.values())
。它不如 Letter.values().stream()
好,但这是数组的问题,而不是具体的枚举。
Moreover, why don't enums have a stream()
method?
你是对的,最好的电话是 Letter.stream()
。不幸的是,class 不能有两个具有相同签名的方法,因此不可能将静态方法 stream()
隐式添加到每个枚举(就像每个枚举都有一个隐式添加的静态方法一样values()
) 因为这会破坏每个现有的枚举,这些枚举已经有一个没有参数的静态或实例方法 stream()
.
Is this approach OK?
我也这么认为。缺点是stream
是一个静态方法,所以没办法避免代码重复;它必须分别添加到每个枚举中。
我会选择 EnumSet
。因为 forEach()
也在 Iterable
上定义,所以您可以完全避免创建流:
EnumSet.allOf(Letter.class).forEach(x -> foo(x));
或方法参考:
EnumSet.allOf(Letter.class).forEach(this::foo);
不过,老派的 for 循环感觉有点简单:
for (Letter x : Letter.values()) {
foo(x);
}
我想知道 Java 8 中使用枚举的所有值的最佳方法是什么。特别是当您需要获取所有值并将其添加到某个地方时,例如,假设我们有以下枚举:
public enum Letter {
A, B, C, D;
}
我当然可以做到以下几点:
for (Letter l : Letter.values()) {
foo(l);
}
但是,我还可以将以下方法添加到枚举定义中:
public static Stream<Letter> stream() {
return Arrays.stream(Letter.values());
}
然后将上面的 for 替换为:
Letter.stream().forEach(l -> foo(l));
这种方法是否可行,或者它在设计或性能上是否存在缺陷?此外,为什么枚举没有 stream() 方法?
我的猜测是枚举的大小是有限的(即大小不受语言限制但受使用限制)因此它们不需要本地 流api。当您必须操作转换并重新收集流中的元素时,流非常有用;这些不是枚举的常见用例(通常你迭代枚举值,但很少需要转换、映射和收集它们)。
如果您只需要对每个元素执行操作,也许您应该只公开一个 forEach 方法
public static void forEach(Consumer<Letter> action) {
Arrays.stream(Letter.values()).forEach(action);
}
.... //example of usage
Letter.forEach(e->System.out.println(e));
三个问题:三部分答案:
从设计的角度来看可以吗?
当然可以。没有错。如果您需要对枚举进行大量迭代,流 API 是一种干净的方法,将样板隐藏在一个小方法后面就可以了。尽管我认为 OldCumudgeon’s version 更好。
从性能的角度来看可以吗?
这很可能无关紧要。大多数时候,枚举并没有那么大。因此,在 99.9% 的情况下,一种方法或另一种方法的开销可能无关紧要。
当然还有0.1%的地方。在那种情况下:使用您的真实数据和消费者进行正确衡量。
如果我不得不打赌,我希望 for each
循环更快,因为它更直接地映射到内存模型,但在谈论性能时不要猜测,也不要调整在实际需要调整之前。以正确的方式编写代码,其次是易于阅读,然后才担心代码风格的性能。
为什么枚举没有正确地集成到流中API?
如果您将 Java 的 Stream API 与许多其他语言中的等效项进行比较,它似乎非常有限。缺少各种部分(例如,可重用的流和作为流的可选)。另一方面,实现 Stream API 对于 API 来说无疑是一个巨大的变化。由于某种原因,它被推迟了多次。所以我猜 Oracle 希望将更改限制在最重要的用例中。无论如何,枚举的使用并不多。当然,每个项目都有几个,但与列表和其他集合的数量相比,它们算不了什么。即使你有一个枚举,在很多情况下你也不会迭代它。另一方面,列表和集合可能几乎每次都被迭代。我认为这些是 Enums 没有得到自己的 Stream 世界适配器的原因。我们将看看在未来的版本中是否会添加更多内容。在那之前你总是可以使用 Arrays.stream
.
我认为获得枚举常量 Stream
的最短代码是 Stream.of(Letter.values())
。它不如 Letter.values().stream()
好,但这是数组的问题,而不是具体的枚举。
Moreover, why don't enums have a
stream()
method?
你是对的,最好的电话是 Letter.stream()
。不幸的是,class 不能有两个具有相同签名的方法,因此不可能将静态方法 stream()
隐式添加到每个枚举(就像每个枚举都有一个隐式添加的静态方法一样values()
) 因为这会破坏每个现有的枚举,这些枚举已经有一个没有参数的静态或实例方法 stream()
.
Is this approach OK?
我也这么认为。缺点是stream
是一个静态方法,所以没办法避免代码重复;它必须分别添加到每个枚举中。
我会选择 EnumSet
。因为 forEach()
也在 Iterable
上定义,所以您可以完全避免创建流:
EnumSet.allOf(Letter.class).forEach(x -> foo(x));
或方法参考:
EnumSet.allOf(Letter.class).forEach(this::foo);
不过,老派的 for 循环感觉有点简单:
for (Letter x : Letter.values()) {
foo(x);
}