17

If you have an Array and you want to use the Java8 forEach() method, which approach is better or more efficient:

Arrays.asList(new String[]{"hallo","hi"}).forEach(System.out::println);

or

Arrays.stream(new String[]{"hallo","hi"}).forEach(System.out::println);

Is the difference significant or are there any better solutions to solve this?

2
  • 1
    Probably asList, which seems to involve fewer objects. But still nothing you'll notice in program execution. Commented Dec 9, 2014 at 23:51
  • 3
    And you can write Arrays.asList("hello","hi") you don't need to create an array if you don't already have one
    – Dici
    Commented Dec 9, 2014 at 23:52

3 Answers 3

20

Neither. If you already had an array,

String[] array;

I would use:

Arrays.stream(array).forEach(System.out::println);

because you leave the conversion of the array to a stream to the JDK - let it be responsible for efficiency etc.

But, since you don't have an array, I would use Stream.of()'s varargs to create a stream of the values:

Stream.of("hallo","hi").forEach(System.out::println);

Which again lets the JDK take the responsibility of efficiently streaming the values as it sees fit.

4
  • I don't understand your If you already had an array. One way or another they need an array. stream takes an array and asList does as well. Commented Dec 10, 2014 at 0:00
  • 5
    @SotiriosDelimanolis "already has an array" means "not coding literal elements in-line" - ie the array was declared and populated externally to the code in question
    – Bohemian
    Commented Dec 10, 2014 at 0:04
  • @SotiriosDelimanolis Yes, readability (always high on the design radar, and I believe in "less code is good") and performance - it hands more over to the JDK (which we tend to trust is "well written").
    – Bohemian
    Commented Dec 10, 2014 at 1:21
  • 3
    To make the decision even harder, I throw in that, if you don’t have an existing array, you can also say Stream.of("hallo","hi").forEach(System.out::println);. That will be more efficient in the special case of length 1 as in that case, no array will be created.
    – Holger
    Commented Dec 10, 2014 at 11:01
14

It seems to make almost absolutely no difference. I created a test class for this. Over the course of five runs, my output was this:

Run 1:
Arrays.asList() method................: 3231 ms
Arrays.stream() method................: 3111 ms
Stream.of() method....................: 3031 ms
Arrays.asList() (premade array) method: 3086 ms
Arrays.stream() (premade array) method: 3231 ms
Stream.of() (premade array) method....: 3191 ms

Run 2:
Arrays.asList() method................: 3270 ms
Arrays.stream() method................: 3072 ms
Stream.of() method....................: 3086 ms
Arrays.asList() (premade array) method: 3002 ms
Arrays.stream() (premade array) method: 3251 ms
Stream.of() (premade array) method....: 3271 ms

Run 3:
Arrays.asList() method................: 3307 ms
Arrays.stream() method................: 3092 ms
Stream.of() method....................: 2911 ms
Arrays.asList() (premade array) method: 3035 ms
Arrays.stream() (premade array) method: 3241 ms
Stream.of() (premade array) method....: 3241 ms

Run 4:
Arrays.asList() method................: 3630 ms
Arrays.stream() method................: 2981 ms
Stream.of() method....................: 2821 ms
Arrays.asList() (premade array) method: 3058 ms
Arrays.stream() (premade array) method: 3221 ms
Stream.of() (premade array) method....: 3214 ms

Run 5:
Arrays.asList() method................: 3338 ms
Arrays.stream() method................: 3174 ms
Stream.of() method....................: 3262 ms
Arrays.asList() (premade array) method: 3064 ms
Arrays.stream() (premade array) method: 3269 ms
Stream.of() (premade array) method....: 3275 ms

From the output, It looks like the Stream.of() method is very marginally (but consistently) the most efficent, and

Stream.of("hallo","hi").forEach(System.out::println);

is very readable code. Stream.of has the advantage in that it doesn't have to convert the array into a list, or create an array and then create a stream, but can create a stream directly from the elements. What was mildly surprising to me, was that because of the way I did my tests, it was faster to instantiate a new array stream each time with Stream.of() than it was to pass in a pre-made array, probably because "capturing" lambdas - those that reference an external variable - are a little less efficient.

Here's the code for my test class:

import java.util.Arrays;
import java.util.function.Consumer;
import java.util.stream.Stream;


public class StreamArrayTest {

    public static void main(String[] args){
        System.out.println("Arrays.asList() method................: " + arraysAsListMethod() + " ms");
        System.out.println("Arrays.stream() method................: " + arraysStreamMethod() + " ms");
        System.out.println("Stream.of() method....................: " + streamOfMethod() + " ms");
        System.out.println("Arrays.asList() (premade array) method: " + presetArraysAsListMethod() + " ms");
        System.out.println("Arrays.stream() (premade array) method: " + presetArraysStreamMethod() + " ms");
        System.out.println("Stream.of() (premade array) method....: " + presetStreamsOfMethod() + " ms");
    }

    private static Long timeOneMillion(Runnable runner){
        MilliTimer mt = MilliTimer.start();
        for (int i = 0; i < 1000000; i++){
            runner.run();
        }
        return mt.end();
    }

    private static Long timeOneMillion(String[] strings, Consumer<String[]> consumer){
        MilliTimer mt = MilliTimer.start();
        for (int i = 0; i < 1000000; i++){
            consumer.accept(strings);
        }
        return mt.end();        
    }

    public static Long arraysAsListMethod(){
        return timeOneMillion(()->Arrays.asList(new String[]{"hallo","hi","test","test2","test3","test4","test5","test6","test7","test8"}).forEach(StreamArrayTest::doSomething));
    }

    public static Long arraysStreamMethod(){
        return timeOneMillion(()->Arrays.stream(new String[]{"hallo","hi","test","test2","test3","test4","test5","test6","test7","test8"}).forEach(StreamArrayTest::doSomething));
    }

    public static Long streamOfMethod(){
        return timeOneMillion(()->Stream.of("hallo","hi","test","test2","test3","test4","test5","test6","test7","test8").forEach(StreamArrayTest::doSomething));        
    }   

    public static Long presetArraysAsListMethod(){
        String[] strings = new String[]{"hallo","hi","test","test2","test3","test4","test5","test6","test7","test8"};
        return timeOneMillion(strings, (s)->Arrays.asList(s).forEach(StreamArrayTest::doSomething));    
    }

    public static Long presetArraysStreamMethod(){
        String[] strings = new String[]{"hallo","hi","test","test2","test3","test4","test5","test6","test7","test8"};
        return timeOneMillion(strings, (s)->Arrays.stream(s).forEach(StreamArrayTest::doSomething));    
    }

    public static Long presetStreamsOfMethod(){
        String[] strings = new String[]{"hallo","hi","test","test2","test3","test4","test5","test6","test7","test8"};
        return timeOneMillion(strings, (s)->Stream.of(s).forEach(StreamArrayTest::doSomething));    
    }

    public static void doSomething(String s){
        String result = s;
        for (int i = 0; i < 10; i++){
            result = result.concat(s);
        }
    }
}

And the MilliTimer class I used:

public class MilliTimer {
    private long startTime = 0L;

    private MilliTimer(long startTime){
        this.startTime = startTime;
    }

    public static MilliTimer start(){
        return new MilliTimer(System.currentTimeMillis());
    }

    public long end() throws IllegalArgumentException {
        return System.currentTimeMillis() - startTime;
    }
}
3
  • The optimizer prefers local values. So a code using a temporary array might indeed be as fast or even faster than that using a prepared array, however, it’s not clear whether this applies to your benchmark as it’s flawed. Your method doSomething may look complicated but actually does nothing as everything inside it has no real effect. A modern JVM is able to recognize that. Besides that, you should use System.nanoTime() rather than System.currentTimeMillis() as the latter returns a value that is not guaranteed to move constantly forward.
    – Holger
    Commented Dec 12, 2014 at 13:28
  • the doSomething method actually does do something... it instantiates a number of String objects and concatenates them together. . If you don't believe it does, then blank out those four lines and watch how the execution time changes from about 3200ms to about 30ms.
    – Steve K
    Commented Dec 14, 2014 at 22:14
  • And if you adjust the number of iterations in the loop inside the doSomething method, that will also affect the execution time.
    – Steve K
    Commented Dec 14, 2014 at 22:30
1
Arrays.asList() method................: 22 ms
Arrays.stream() method................: 26 ms
Stream.of() method....................: 26 ms
Arrays.asList() (premade array) method: 8 ms
Arrays.stream() (premade array) method: 30 ms
Stream.of() (premade array) method....: 17 ms

When you change doSomething to actually do nothing as follows:

public static void doSomething(String s){
}

Then you're measuring the actual speed of these operations instead of the operation String = String + String; This is what doSomething was doing, and of course it is about the same speed consistently. However, the actual speed isn't the same and asList with a premade array is much faster.

The real result here has already been noted by others that you should beware of the stream as it is typically 4 times slower than the plain old java (non-lambda) approach.

1
  • 1
    If you remove the body of doSomething, you're not taking into account input/output of the various processing methods. Yes, a list can be instantiated using Arrays.asList() faster than Stream.of() or Arrays.stream() but if you don't actually DO anything with the values in the array, there's no processing overhead because the optimizer will recognize the noop method and cut it out. The times for doSomething() should be consistent because they're the exact same operations, and what's left should mostly be overheads from invoking the various processing methods.
    – Steve K
    Commented Aug 8, 2019 at 0:28

Not the answer you're looking for? Browse other questions tagged or ask your own question.