Java 8流和可变参数

23

根据Effective Java第2版,当你想编写一个允许使用可变参数但在编译时仍强制至少有一个元素的方法签名时,应该以以下方式编写方法签名:

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 的流并将其与另一个流连接起来。有没有更简洁的方法来做到这一点?


7
对我来说,“Stream.concat”看起来还不错...它简洁明了,没有创建数据的副本。你只是创建了一些额外的包装对象。在我看来真的没什么问题。在Java编程中,你只需要为此创建一些小对象。与使用“ArrayList”合并它们相比,“Stream.concat”已经领先了很多。 - Radiodef
1
我同意Radiodef的观点,如果你真的想要的话,你可以自己实现Spliterator<T>并将其与StreamSupport.stream(spliterator, parallel)一起使用,但我真的不认为这会使它更易读或更高效。 - ug_
我认为这个标题不够具体。 - kio21
5个回答

11

以下是一种方法,可以在不创建两个Streams的情况下完成,尽管您可能不喜欢它。

Stream.Builder<String> builder = Stream.<String>builder().add(required);
for (String s : additional) {
  builder.add(s);
}

Stream<String> allParams = builder.build();

2
您也可以使用Stream.of(additional).forEach(builder);来替换for循环。 - Tagir Valeev
经过重新审视和根据评论,我原来的方法似乎还不错。然而,考虑到我的先前观点,这个答案似乎是最好的替代方案。谢谢 :) - Niko

7

组合的流没有问题。这些对象很轻量,因为它们只引用源数据而不像数组内容那样复制数据。这种轻量级对象的成本可能只有在实际有效载荷也非常小的情况下才相关。这种情况可以通过专门的、语义等效的重载来处理:

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是应用程序的一部分而不是库,则不应添加它们,除非分析器告诉您该参数处理引起了瓶颈。


2

Stream API的第三方扩展,如我的StreamExjOOλ提供了像“append”或“prepend”这样的方法,可以更加清晰地完成这项操作:

// Using StreamEx
Stream<String> allParams = StreamEx.of(required).append(additional);
// Using jOOL
Stream<String> allParams = Seq.of(required).append(additional);

2

不幸的是,Java 可能会非常冗长。但缓解这种情况的另一种选择是简单地使用静态导入。在我看来,它并不会使您的代码变得不清晰,因为每个方法都与流相关。

Stream<String> allParams =
    concat(of(required), of(additional));

我个人不喜欢静态导入 - 这只是又一个阻碍我们知道某个东西来自哪里(在这种情况下是静态函数)的机制。 - absmiths

2
如果您愿意使用Guava,可以使用Lists.asList(required, additional).stream()。该方法旨在减轻具有最小要求成语的varargs。
顺便说一句,我认为这个库非常有用,但当然仅仅因为这个原因添加它并不是一个好主意。请查看文档,看看它是否对您更有用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接