17

Java 8 streams allow code that is a lot more readable than old-fashioned for loops, in most cases. However, based on my own experience and what I've read, using a stream instead of a for loop can involve a performance hit (or occasionally an improvement) which is sometimes difficult to predict.

In a large project it doesn't seem feasible to write a benchmark test for every loop, so when deciding whether to replace a for loop with a stream, what are the key factors (e.g. expected size of the collection, expected percentage of values removed by filtering, complexity of iterative operations, the type of reduction or aggregation, etc.) which give a likely indication of the performance change that will result?

Note: this is a narrowing of my earlier question, which was closed for being too broad (and for which the aspects of parallel streams were pretty well covered in another SO question), so let's just limit this to sequential streams.

Community
  • 1
  • 1
aro_tech
  • 1,103
  • 7
  • 20
  • 15
    For 99% of cases, the answer is simple: write the code that is the clear, readable, maintainable, and obviously correct. While one can fret endlessly about performance, in most cases, the actual different in cost, when measured in dollars, is zero (because most code is "fast enough.") On the other hand, getting it wrong can have a real dollar cost. (And, if you're in that less than 1% that needs to fret about performance, you already know it, and you've already made a big investment in measurement methodologies.) – Brian Goetz Jan 06 '16 at 21:06
  • Thanks to those who answered or commented. My question was based on an article in a French magazine (Programmez, Jan. 2016) where some consultants branched the code of a B2C site and refactored to replace many `for` loops with streams. Based on the performance tests (method execution time roughly doubled in the worst cases), they were unable to convince the product owner to proceed with the refactoring in the main branch. It's hard to sell a product owner on the idea of "We're going to spend a week improving the code. We'll be adding no features, and the product will run more slowly." – aro_tech Jan 07 '16 at 14:56

2 Answers2

27

It’s not only “not feasible to write a benchmark test for every loop”, it’s counter productive. A particular, application specific loop may perform entirely different when being put into a micro-benchmark.

For an actual application, the standard rule of optimization applies: don’t do it. Just write whatever is more readable and only if there is a performance problem, profile the entire application to check whether a particular loop or stream use really is the bottleneck. Only if this is the case, you may try to switch between both idioms at the particular bottleneck to see whether it makes a difference.

In most cases, it won’t. If there is a real performance issue, it will stem from the type of operation, e.g. performing a nested iteration with an O(n²) time complexity, etc. Such problems do not dependent on whether you use a Stream or a for loop and the minor performance differences between these two idioms don’t change how your code scales.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 1
    After some thought, I decided to choose this as the answer. The question received 2 well-written, well-reasoned answers as well as an intelligent comment from a world-renowned expert. I wanted an answer I could use in code reviews to catch a 2x slowdown pre-commit, but maybe that is not possible. I chose this answer because apparently the best approach to replacing loops with streams in performance-sensitive code is to do it in an incremental way which can be easily backed out if a performance issue is detected by automated end-to-end tests. – aro_tech Jan 08 '16 at 09:28
7

There aren't big general speed differences between streams and loops; their advantages/disadvantages are problem-specific. Whether you choose one or the other should depend (mostly) on the readability the code. For some performance comparisons, see Benchmark1 and Benchmark2 where you can notice Brian Goetz's comment to one of the answers:

Your conclusion about performance, while valid, is overblown. There are plenty of cases where the stream code is faster than the iterative code, largely because per-element access costs is cheaper with streams than with plain iterators. And in many cases, the streams version inlines to something that is equivalent to the hand-written version. Of course, the devil is in the details; any given bit of code might behave differently.

Apart from that, just make sure that when you benchmark you use the JMH.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
AlexGuevara
  • 932
  • 11
  • 28