Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Learn Performance in Stream API | Practical Applications of Stream API
Stream API
course content

Course Content

Stream API

Stream API

1. Fundamentals and Functional Capabilities of Stream API
4. Practical Applications of Stream API

book
Performance in Stream API

You successfully refactored the code, making it cleaner, shorter, and more expressive. But what about performance? How efficient is Stream API compared to traditional loops? Can you speed it up using parallelStream()? Let's find out!

Measuring Execution Time

To objectively compare performance, we'll create a test dataset with 100,000 users and different orders. Then, we'll implement three filtering approaches:

  • Traditional for-loop – a classic approach with nested loops;
  • Stream API (stream()) – a modern declarative method;
  • Parallel Stream API (parallelStream()) – multithreaded processing.

You'll measure execution time using System.nanoTime(), which provides high-precision time differences.

Test Implementation

You'll generate 100,000 users, ensuring they all have orders above $10,000, and run all three approaches to see which one performs the best.

java

Main

copy
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = generateUsers(100000); // Measuring traditional loop execution time long startTime = System.nanoTime(); List<User> premiumUsersLoop = new ArrayList<>(); for (User user : users) { if (user.isActive()) { int totalOrders = 0; for (Order order : user.getOrders()) { if (order.getTotal() >= 10000) { totalOrders++; } } if (totalOrders >= 3) { premiumUsersLoop.add(user); } } } long endTime = System.nanoTime(); System.out.println("For-loop: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersStream = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Parallel Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersParallelStream = users.parallelStream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Parallel Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); } private static List<User> generateUsers(int count) { List<User> users = new ArrayList<>(); for (int i = 0; i < count; i++) { users.add(new User("User" + i, true, List.of( new Order(14000), new Order(5000), new Order(7000) ))); } return users; } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }

After running the test with 100,000 users, you might observe the following trends:

Traditional for-loop is faster for simple operations since it avoids overhead from stream creation and additional objects. It performs best when raw speed is a priority.

Stream API is sometimes slightly slower due to additional stream objects, but it significantly improves code readability and maintainability.

Parallel Stream API can speed up processing on multi-core systems, but not always. If the dataset is small, the overhead of managing threads can actually slow it down. It works best for heavy computations, but not when modifying shared variables, as threads run independently.

Summary

Stream API is a powerful tool that makes code more readable and concise. However, when it comes to performance, it's important to understand its limitations. In some cases, traditional for-loops are faster, especially when working with small datasets. parallelStream() can improve processing speed, but it requires testing to ensure it actually provides a benefit.

So, choosing the right approach should be intentional: if readability is a priority, use Stream API; if performance is critical, test and measure!

1. Which approach is typically the fastest for simple operations?

2. Why can Stream API be slower than a regular loop?

3. When can parallelStream() slow down execution?

question mark

Which approach is typically the fastest for simple operations?

Select the correct answer

question mark

Why can Stream API be slower than a regular loop?

Select the correct answer

question mark

When can parallelStream() slow down execution?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

Section 4. Chapter 2
We're sorry to hear that something went wrong. What happened?
some-alt