Performance Comparison of Java Stream API vs Iterator and Recommendations
This article explains Java 8 Stream fundamentals, compares its intermediate and terminal operations with traditional iterator loops through a series of benchmark tests (mapping, filtering, sorting, reduction, string joining, and mixed operations), analyzes results for different data sizes and CPU configurations, and provides practical guidance on when to use Stream, parallel Stream, or iterator in backend development.
Java 8 introduced the Stream API (java.util.stream) as a new abstraction for processing sequences of elements, offering specialized streams such as IntStream, LongStream, and DoubleStream. Streams replace many collection operations, providing a fluent, functional style with internal iteration.
The article outlines two categories of stream operations: intermediate operations (e.g., filter, distinct, map, sorted) that return another stream and can be chained, and terminal operations that produce a result or a collection, array, or string.
Key characteristics of streams are highlighted: they can be traversed only once, use internal iteration (generally more efficient than external iterator loops), support lazy evaluation, can represent infinite sequences, and lead to more concise code.
Advantages over traditional collections include no storage of elements (the stream pulls data from the source), functional programming style, lazy evaluation that enables short‑circuiting, ability to handle infinite streams, and reduced boilerplate.
Benchmark setup
System: Ubuntu 16.04 xenial<br/>CPU: Intel Core i7-8550U<br/>RAM: 16GB<br/>JDK version: 1.8.0_151<br/>JVM: HotSpot(TM) 64‑Bit Server VM (build 25.151‑b12, mixed mode)<br/>JVM Settings:<br/> -Xms1024m<br/> -Xmx6144m<br/> -XX:MaxMetaspaceSize=512m<br/> -XX:ReservedCodeCacheSize=1024m<br/> -XX:+UseConcMarkSweepGC<br/> -XX:SoftRefLRUPolicyMSPerMB=100The following tests were performed on random integer lists with sizes ranging from 10 to 10,000,000, each repeated ten times and averaged:
1. Mapping test
// stream<br/>List<Integer> result = list.stream()<br/> .mapToInt(x -> x)<br/> .map(x -> ++x)<br/> .boxed()<br/> .collect(Collectors.toCollection(ArrayList::new));<br/><br/>// iterator<br/>List<Integer> result = new ArrayList<>();<br/>for (Integer e : list) {<br/> result.add(++e);<br/>}<br/><br/>// parallel stream<br/>List<Integer> result = list.parallelStream()<br/> .mapToInt(x -> x)<br/> .map(x -> ++x)<br/> .boxed()<br/> .collect(Collectors.toCollection(ArrayList::new));2. Filtering test
// stream<br/>List<Integer> result = list.stream()<br/> .mapToInt(x -> x)<br/> .filter(x -> x > 200)<br/> .boxed()<br/> .collect(Collectors.toCollection(ArrayList::new));<br/><br/>// iterator<br/>List<Integer> result = new ArrayList<>(list.size());<br/>for (Integer e : list) {<br/> if (e > 200) {<br/> result.add(e);<br/> }<br/>}<br/><br/>// parallel stream<br/>List<Integer> result = list.parallelStream()<br/> .mapToInt(x -> x)<br/> .filter(x -> x > 200)<br/> .boxed()<br/> .collect(Collectors.toCollection(ArrayList::new));3. Natural sorting test
// stream<br/>List<Integer> result = list.stream()<br/> .mapToInt(x -> x)<br/> .sorted()<br/> .boxed()<br/> .collect(Collectors.toCollection(ArrayList::new));<br/><br/>// iterator<br/>List<Integer> result = new ArrayList<>(list);<br/>Collections.sort(result);<br/><br/>// parallel stream<br/>List<Integer> result = list.parallelStream()<br/> .mapToInt(x -> x)<br/> .sorted()<br/> .boxed()<br/> .collect(Collectors.toCollection(ArrayList::new));4. Reduction (max) test
// stream<br/>int max = list.stream()<br/> .mapToInt(x -> x)<br/> .max()<br/> .getAsInt();<br/><br/>// iterator<br/>int max = -1;<br/>for (Integer e : list) {<br/> if (e > max) { max = e; }<br/>}<br/><br/>// parallel stream<br/>int max = list.parallelStream()<br/> .mapToInt(x -> x)<br/> .max()<br/> .getAsInt();5. String joining test
// stream<br/>String result = list.stream()<br/> .map(String::valueOf)<br/> .collect(Collectors.joining(","));<br/><br/>// iterator<br/>StringBuilder builder = new StringBuilder();<br/>for (Integer e : list) {<br/> builder.append(e).append(",");<br/>}<br/>String result = builder.length() == 0 ? "" : builder.substring(0, builder.length() - 1);<br/><br/>// parallel stream<br/>String result = list.parallelStream()<br/> .map(String::valueOf)<br/> .collect(Collectors.joining(","));6. Mixed operations test
// stream<br/>List<Integer> result = list.stream()<br/> .filter(Objects::nonNull)<br/> .mapToInt(x -> x + 1)<br/> .filter(x -> x > 200)<br/> .distinct()<br/> .boxed()<br/> .collect(Collectors.toCollection(ArrayList::new));<br/><br/>// iterator<br/>HashSet<Integer> set = new HashSet<>(list.size());<br/>for (Integer e : list) {<br/> if (e != null && e > 200) {<br/> set.add(e + 1);<br/> }<br/>}<br/>List<Integer> result = new ArrayList<>(set);<br/><br/>// parallel stream<br/>List<Integer> result = list.parallelStream()<br/> .filter(Objects::nonNull)<br/> .mapToInt(x -> x + 1)<br/> .filter(x -> x > 200)<br/> .distinct()<br/> .boxed()<br/> .collect(Collectors.toCollection(ArrayList::new));Experimental results
For small data sets (size ≤ 1 000), traditional iterator loops are slightly faster than Stream, but the difference is sub‑millisecond and negligible for most business logic; Stream provides much cleaner code. For larger data sets (size > 10 000), Stream—especially parallel Stream—outperforms iterator, leveraging multiple CPU cores for significant speed gains, though parallel Stream’s benefit depends on the actual core allocation and may incur overhead on single‑core machines.
Recommendations
Use iterator for simple, single‑step loops.
Prefer Stream for multi‑step processing to gain readability with minimal performance loss.
Avoid parallel Stream on single‑core environments; use it when multiple cores and large data volumes are available.
When processing primitive streams, convert to IntStream, LongStream, or DoubleStream early to reduce boxing/unboxing overhead.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
