Java 8 streams and varargs

20,730

Solution 1

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

Solution 2

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.

Solution 3

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

Solution 4

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.

Solution 5

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);
Share:
20,730

Related videos on Youtube

Paul FREAKN Baker
Author by

Paul FREAKN Baker

Updated on April 13, 2020

Comments

  • Paul FREAKN Baker
    Paul FREAKN Baker about 4 years

    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?

    • Radiodef
      Radiodef about 8 years
      Stream.concat seems fine to me...it's short, concise, it doesn't create copies of the data. You're creating a few extra wrapper objects, that's all. IMO really nothing wrong with it. Programming in Java, you just have to create small objects for things here and there. Stream.concat is already miles ahead of merging them with an ArrayList or something like that.
    • ug_
      ug_ about 8 years
      I agree with Radiodef, if you really wanted to you could make your own implementation of Spliterator<T> and use it with StreamSupport.stream(spliterator, parallel) but i really dont feel like that makes it any more readable or efficient.
    • kio21
      kio21 over 4 years
      I think this title is not concrete.
  • Tagir Valeev
    Tagir Valeev about 8 years
    You may also replace for-loop with Stream.of(additional).forEach(builder);
  • Paul FREAKN Baker
    Paul FREAKN Baker almost 7 years
    Upon revisiting this and based on the comments, my original way seems fine. However, this answer seems to be the best alternative given my previous view. Thanks :)
  • absmiths
    absmiths almost 5 years
    I personally don't like static imports - it's just one more mechanism that gets in the way of knowing where something came from (in this case the static function).