简体   繁体   中英

Java 8 streams and varargs

According to Effective Java 2nd Ed , when you want to write a method signature that allows for varargs but still enforces that you have one element minimum at compile-time you should write the method signature this way:

public void something(String required, String ... additional) {
    //... do what you want to do
}

If I want to stream all these elements, I've been doing something like this:

public void something(String required, String ... additional) {
    Stream<String> allParams =
        Stream.concat(Stream.of(required), Stream.of(additional));
    //... do what you want to do
}

This feels really inelegant and wasteful, especially because I'm creating a stream of 1 and concatenating it with another. Is there a cleaner way to do this?

Here is a way for doing it without creating two Streams , although you might not like it.

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

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

Unfortunately, Java can be quite verbose. But another option to alleviate that is to simply use static imports. In my opinion, it does not make your code less clear since every method is stream-related.

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

There is nothing wrong with the composed streams. These objects are lightweight as they only refer to the source data but don't copy data like array contents. The cost of such lightweight object might only be relevant if the actual payload is very small as well. Such scenarios can be handled with specialized, semantically equivalent overloads:

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
}

so in the case of only one argument you're not only saving Stream instances but also the varargs array (similar to Stream.of 's overload). This is a common pattern, see for example the EnumSet.of overloads.

However, in a lot of cases even these simple overloads are not necessary and might be considered premature optimization (libraries like the JRE offer them as it's otherwise impossible for an application developer to add them if ever needed). If something is part of an application rather than a library you shouldn't add them unless a profiler tells you that there's a bottleneck caused by that parameter processing.

If you're willing to use Guava, you may Lists.asList(required, additional).stream() . The method was created to ease that varargs with minimum requirement idiom.

A side note, I consider the library really useful, but of course it's not a good idea to add it just because of that. Check the docs and see if it could be of more use to you.

Third-party extensions to Stream API like my StreamEx or jOOλ provide methods like append or prepend which allow you to do this in more clean way:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM