Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 591
Paul Nelson Baker Avatar asked Apr 05 '16 21:04

Paul Nelson Baker


People also ask

What is Java Varargs?

Varargs is a short name for variable arguments. In Java, an argument of a method can accept arbitrary number of values. This argument that can accept variable number of values is called varargs. The syntax for implementing varargs is as follows: accessModifier methodName(datatype… arg) { // method body }

Is it good to use varargs in Java?

Varargs are useful for any method that needs to deal with an indeterminate number of objects. One good example is String. format . The format string can accept any number of parameters, so you need a mechanism to pass in any number of objects.

How do you handle varargs in Java?

Important Points regarding Varargs Before JDK 5, variable length arguments could be handled in two ways: One was using overloading, other was using array argument. There can be only one variable argument in a method. Variable argument (Varargs) must be the last argument.

Can Varargs be used in abstract method?

So nope, the forwarder definitely isn't getting implemented. Putting the annotation on both bar methods fails to compile: A method with a varargs annotation produces a forwarder method with the same signature (args: Array[String])Unit as an existing method.


2 Answers

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();
like image 76
Sleiman Jneidi Avatar answered Sep 28 '22 21:09

Sleiman Jneidi


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.

like image 32
Holger Avatar answered Sep 28 '22 21:09

Holger