Java 8 个流和可变参数
Java 8 streams and varargs
根据 Effective Java 第二版 ,当你想要编写一个允许可变参数但仍然强制你在编译时至少有一个元素的方法签名时 -是时候你应该这样写方法签名了:
public void something(String required, String ... additional) {
//... do what you want to do
}
如果我想流式传输所有这些元素,我一直在做这样的事情:
public void something(String required, String ... additional) {
Stream<String> allParams =
Stream.concat(Stream.of(required), Stream.of(additional));
//... do what you want to do
}
这感觉真的很不雅和浪费,特别是因为我正在创建一个流 1 并将它与另一个连接起来。有更简洁的方法吗?
这是一种无需创建两个 Streams
的方法,尽管您可能不喜欢它。
Stream.Builder<String> builder = Stream.<String>builder().add(required);
for (String s : additional) {
builder.add(s);
}
Stream<String> allParams = builder.build();
如果您愿意使用 Guava,可以Lists.asList(required, additional).stream()
。创建该方法是为了简化具有最低要求习语的可变参数。
旁注,我认为该库确实很有用,但当然,仅仅因为这个而添加它并不是一个好主意。检查 docs 看看它是否对您更有用。
不幸的是,Java 可能非常冗长。但另一种缓解这种情况的方法是简单地使用静态导入。在我看来,这不会使您的代码变得不那么清晰,因为每个方法都与流相关。
Stream<String> allParams =
concat(of(required), of(additional));
组合流没有问题。这些对象是轻量级的,因为它们只引用源数据,而不像数组内容那样复制数据。这种轻量级对象的成本可能只有在实际有效载荷也非常小的情况下才有意义。这种情况可以用专门的、语义上等效的重载来处理:
public void something(String required, String ... additional) {
somethingImpl(Stream.concat(Stream.of(required), Stream.of(additional)));
}
public void something(String required) {
somethingImpl(Stream.of(required));
}
public void something(String required, String second) {
somethingImpl(Stream.of(required, second));
}
private void somethingImpl(Stream<String> allParams) {
//... do what you want to do
}
所以在只有一个参数的情况下,您不仅保存了 Stream
个实例,还保存了可变参数数组(类似于 Stream.of
的重载)。这是一个常见的模式,例如 EnumSet.of
重载。
然而,在很多情况下,即使是这些简单的重载也不是必需的,并且可能被认为是过早的优化(像 JRE 这样的库提供了它们,否则应用程序开发人员不可能在需要时添加它们)。如果 something
是应用程序的一部分而不是库的一部分,则不应添加它们,除非探查器告诉您该参数处理存在瓶颈。
Stream API 的第三方扩展,例如我的 StreamEx or jOOλ 提供了 append
或 prepend
等方法,让您可以更简洁地执行此操作:
// Using StreamEx
Stream<String> allParams = StreamEx.of(required).append(additional);
// Using jOOL
Stream<String> allParams = Seq.of(required).append(additional);
根据 Effective Java 第二版 ,当你想要编写一个允许可变参数但仍然强制你在编译时至少有一个元素的方法签名时 -是时候你应该这样写方法签名了:
public void something(String required, String ... additional) {
//... do what you want to do
}
如果我想流式传输所有这些元素,我一直在做这样的事情:
public void something(String required, String ... additional) {
Stream<String> allParams =
Stream.concat(Stream.of(required), Stream.of(additional));
//... do what you want to do
}
这感觉真的很不雅和浪费,特别是因为我正在创建一个流 1 并将它与另一个连接起来。有更简洁的方法吗?
这是一种无需创建两个 Streams
的方法,尽管您可能不喜欢它。
Stream.Builder<String> builder = Stream.<String>builder().add(required);
for (String s : additional) {
builder.add(s);
}
Stream<String> allParams = builder.build();
如果您愿意使用 Guava,可以Lists.asList(required, additional).stream()
。创建该方法是为了简化具有最低要求习语的可变参数。
旁注,我认为该库确实很有用,但当然,仅仅因为这个而添加它并不是一个好主意。检查 docs 看看它是否对您更有用。
不幸的是,Java 可能非常冗长。但另一种缓解这种情况的方法是简单地使用静态导入。在我看来,这不会使您的代码变得不那么清晰,因为每个方法都与流相关。
Stream<String> allParams =
concat(of(required), of(additional));
组合流没有问题。这些对象是轻量级的,因为它们只引用源数据,而不像数组内容那样复制数据。这种轻量级对象的成本可能只有在实际有效载荷也非常小的情况下才有意义。这种情况可以用专门的、语义上等效的重载来处理:
public void something(String required, String ... additional) {
somethingImpl(Stream.concat(Stream.of(required), Stream.of(additional)));
}
public void something(String required) {
somethingImpl(Stream.of(required));
}
public void something(String required, String second) {
somethingImpl(Stream.of(required, second));
}
private void somethingImpl(Stream<String> allParams) {
//... do what you want to do
}
所以在只有一个参数的情况下,您不仅保存了 Stream
个实例,还保存了可变参数数组(类似于 Stream.of
的重载)。这是一个常见的模式,例如 EnumSet.of
重载。
然而,在很多情况下,即使是这些简单的重载也不是必需的,并且可能被认为是过早的优化(像 JRE 这样的库提供了它们,否则应用程序开发人员不可能在需要时添加它们)。如果 something
是应用程序的一部分而不是库的一部分,则不应添加它们,除非探查器告诉您该参数处理存在瓶颈。
Stream API 的第三方扩展,例如我的 StreamEx or jOOλ 提供了 append
或 prepend
等方法,让您可以更简洁地执行此操作:
// Using StreamEx
Stream<String> allParams = StreamEx.of(required).append(additional);
// Using jOOL
Stream<String> allParams = Seq.of(required).append(additional);