When not to use java8 streams?

Viewed 1909

I was just trying out a few code snippets and came across an observation where using simple for loop gave me better performance results compared to using java 8 stream. Now, I may have missed something in understanding these things. I need help understanding the difference. Adding my code below.

//Following takes almost 3ms

public int[] testPerf(int[] nums, int[] index) {
    List<Integer> arrayL = new ArrayList<>();
    for(int i =0; i< index.length; i++){
        arrayL.add(index[i], nums[i]);
    }
    return arrayL.stream().mapToInt(i -> i).toArray();
}

//Following takes almost 1ms

public int[] testPerf(int[] nums, int[] index) {
    List<Integer> arrayL = new ArrayList<>();
    for(int i =0; i< index.length; i++){
        arrayL.add(index[i], nums[i]);
    }
    int [] result = new int[index.length];
    for(int i =0; i< index.length; i++){
        result[i] = arrayL.get(i);
    }
    return result;
}

EDIT: START

What am I trying to test? Injecting elements of nums at indexes specified by index array to form a final result.

Input: nums = [0,1,2,3,4], index = [0,1,2,2,1]
Output: [0,4,1,3,2]
Explanation:
nums       index     target
0            0        [0]
1            1        [0,1]
2            2        [0,1,2]
3            2        [0,1,3,2]
4            1        [0,4,1,3,2]

EDIT: END

Note that I have tested these with different inputs and tried arrays containing 2k+ elements but still the code with for loop gave me better performance.

Please guide what is it that makes the other code take more time? Also point out to any references where I can learn when NOT to use java streams? (When not to overcomplicate? :) )

2 Answers

I use streams over loops (I use anything over loops), when I can, because streams a usually much easier to write without bugs, even in absence of tests, because of their declarative uniform syntax. For the same reason they are usually much easier to read for a non-author, even without thorough commenting. Also, streams are lazy (think "pull"), for loops are eager (think "push"). And being lazy is usually better.

Unfortunately, streams have (sometimes substantial) overhead, so need to pay attention when writing performance critical code, especially with collectors.

So, something like "use streams when you can and loops where you must".

The goal of streams is to make the code compacter, better readable, to reduce boiler plate and thus to simplify the developer's work. The goal of streams is not to give performance gains. The performance in a specific case can differ from for. Also streams can take a bit more memory. But the result - how streams are converted to a byte code - can depend on JVM: Oracle JDK, Open JDK, IBM JDK, etc.

If you have strict requirements for memory and performance, then there is no ready answer. You should compare your productive code (not the example you show here) in both variants - with for and with streams, then choose what fits your strict requirements.

But...

But in the most applications now days the impact of the loops, no matter is it for or streams, on performance is very low compared to other operations like accessing database or calling some web services via network. Even if for would be x10 faster compared to streams, but database access takes 2 000 ms, you would not see real difference.

Where as difference in the code style can be essential. Developers can produce code that is better readable. When some developers in your team go to other projects and some new join your team and have to extend the existing code, they will need less time to understand such code. It is not that understanding for is hard :) But reading code with multiple streams feels to many developers naturals compared to the code with for.

There are a few special cases where streams are hard to apply, like loop over 2 or 3 collections simultaneously. In such case streams make not much sense, for is preferable.

But in general there are no rules when to use or not to use specific construct. You know that any while and do - while loop can be replaced with a for loop. But why Java has different types of loops? Because some of them can express the logic in particular cases in a better way that other the others. Same with streams. If you and your team don't feel comfortable with streams, you don't have to use them. Give them a try, use them over 3 months, then discuss within the team and decide if every developer in your project should use them as much as possible or should try to avoid.

Related