Partner – Microsoft – NPI EA (cat = Baeldung)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Microsoft – NPI EA (cat= Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, you can get started over on the documentation page.

And, you can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat=Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag=Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – MongoDB – NPI EA (tag=MongoDB)
announcement - icon

Traditional keyword-based search methods rely on exact word matches, often leading to irrelevant results depending on the user's phrasing.

By comparison, using a vector store allows us to represent the data as vector embeddings, based on meaningful relationships. We can then compare the meaning of the user’s query to the stored content, and retrieve more relevant, context-aware results.

Explore how to build an intelligent chatbot using MongoDB Atlas, Langchain4j and Spring Boot:

>> Building an AI Chatbot in Java With Langchain4j and MongoDB Atlas

Partner – LambdaTest – NPI EA (cat=Testing)
announcement - icon

Accessibility testing is a crucial aspect to ensure that your application is usable for everyone and meets accessibility standards that are required in many countries.

By automating these tests, teams can quickly detect issues related to screen reader compatibility, keyboard navigation, color contrast, and other aspects that could pose a barrier to using the software effectively for people with disabilities.

Learn how to automate accessibility testing with Selenium and the LambdaTest cloud-based testing platform that lets developers and testers perform accessibility automation on over 3000+ real environments:

Automated Accessibility Testing With Selenium

eBook – Java Streams – NPI (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

1. Introduction

In this tutorial, we’ll dive deep into a Java Streams vs. For-Loops comparison. These tools play a vital role in data processing for every Java developer. Although they are different in many ways, as we’ll see in the rest of the article, they have very similar use cases and can be easily interchangeable many times.

Streams, introduced in Java 8, offer a functional and declarative approach, while for-loops provide a traditional imperative method. By the end of the article, we can make the most suitable decision for our programming tasks.

2. Performance

When it comes to comparing solutions to a particular programming problem, we often have to talk about performance. Also, this case is no different. Since both streams and for-loops are used to process large amounts of data, performance can be important in choosing the right solution.

Let’s walk through a comprehensive benchmarking example to understand the performance differences between for-loops and streams. We’ll compare the execution times of complex operations involving filtering, mapping, and summing using both for-loops and streams. For this purpose, we’ll use the Java Microbenchmarking Harness (JMH), a tool designed specifically for benchmarking Java code.

2.1. Getting Started

We start by defining the dependencies:

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.37</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.37</version>
</dependency>

We can always find the latest versions of JMH Core and JMH Annotation Processor on Maven Central.

2.2. Setting Up the Benchmark

In our benchmark, we’ll create a scenario with a list of integers ranging from 0 to 999,999. We want to filter out even numbers, square them, and then calculate their sum. Besides that, to ensure fairness, we’ll first implement this process using a traditional for-loop:

@State(Scope.Thread)
public static class MyState {
    List<Integer> numbers;

    @Setup(Level.Trial)
    public void setUp() {
        numbers = new ArrayList<>();
        for (int i = 0; i < 1_000_000; i++) {
            numbers.add(i);
        }
    }
}

This State class will be passed to our benchmark. Also, the Setup will run before each of them.

2.3. Benchmarking with For-Loops

Our for-loop implementation involves iterating through the list of numbers, checking for evenness, squaring them, and accumulating the sum in a variable:

@Benchmark
public int forLoopBenchmark(MyState state) {
    int sum = 0;
    for (int number : state.numbers) {
        if (number % 2 == 0) {
            sum = sum + (number * number);
        }
    }
    return sum;
}

2.4. Benchmarking with Streams

Next, we’ll implement the same complex operations using Java streams. Moreover, we’ll begin by filtering the even numbers, mapping them to their squares, and ultimately calculating the sum:

@Benchmark
public int streamBenchMark(MyState state) {
    return state.numbers.stream()
      .filter(number -> number % 2 == 0)
      .map(number -> number * number)
      .reduce(0, Integer::sum);
}

We use the terminal operations reduce() to compute the sum of the numbers. Also, we can calculate the sum in multiple ways.

2.5. Running the Benchmark

With our benchmark methods in place, we’ll run the benchmark using JMH. We’ll execute the benchmark multiple times to ensure accurate results and measure the average execution time. To do this, we’ll add the following annotations to our class:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)

With these additions, we ensure the result would be more accurate, running the benchmark five times, after three warmups and computing the average of all five iterations. Now, we can run the main method to see the results:

public static void main(String[] args) throws RunnerException {
    Options options = new OptionsBuilder()
      .include(PerformanceBenchmark.class.getSimpleName())
      .build();
    new Runner(options).run();
}

2.6. Analyzing the Results

Once we run the benchmark, JMH will provide us with average execution times for both the for-loop and stream implementations:

Benchmark                              Mode  Cnt         Score         Error  Units
PerformanceBenchmark.forLoopBenchmark  avgt    5   3386660.051 ± 1375112.505  ns/op
PerformanceBenchmark.streamBenchMark   avgt    5  12231480.518 ± 1609933.324  ns/op

We can see that in our example, the for-loops performed much better than the streams from the performance perspective. Even though streams performed worse than for-loops in this example, this could change in some cases, especially with parallel streams.

3. Syntax and Readability

As programmers, the readability of our code plays an important role. Because of this, this aspect becomes an important one when we try to choose the best solution for our problem.

First and foremost, let’s dive into the syntax and readability of streams. Streams promote a more concise and expressive style of coding. This is evident when filtering and mapping data:

List<String> fruits = Arrays.asList("apple", "banana", "orange", "grape");
long count = fruits.stream()
  .filter(fruit -> fruit.length() > 5)
  .count();

The stream code reads like a fluent sequence of operations, with the filtering condition and the count operation clearly expressed in a single, fluid chain. Furthermore, streams often result in code that’s easier to read due to their declarative nature. The code focuses more on what needs to be done than how to do it.

In contrast, let’s explore the syntax and readability of for-loops. for-loops provide a more traditional and imperative style of coding:

List<String> fruits = Arrays.asList("apple", "banana", "orange", "grape");
long count = 0;
for (String fruit : fruits) {
    if (fruit.length() > 5) {
        count++;
    }
}

Here, the code involves explicit iteration and conditional statements. While this approach is well-understood by most developers, it can sometimes lead to more verbose code, making it potentially harder to read, especially for complex operations.

4. Parallelism and Concurrency

Parallelism and concurrency are crucial aspects to consider when comparing streams and for-loops in Java. Both approaches offer different capabilities and challenges when utilizing multi-core processors and managing concurrent operations.

Streams are designed to make parallel processing more accessible. Java 8 introduced the concept of parallel streams, which automatically leverage multi-core processors to speed up data processing. We can easily rewrite the benchmark from the previous point to compute the sum concurrently:

@Benchmark
public int parallelStreamBenchMark(MyState state) {
    return state.numbers.parallelStream()
      .filter(number -> number % 2 == 0)
      .map(number -> number * number)
      .reduce(0, Integer::sum);
}

The only thing needed to parallelize the process is to replace stream() with parallelStream() method. On the other side, rewriting the for-loop to compute the sum of numbers in parallel is more complicated:

@Benchmark
public int concurrentForLoopBenchmark(MyState state) throws InterruptedException, ExecutionException {
    int numThreads = Runtime.getRuntime().availableProcessors();
    ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
    List<Callable<Integer>> tasks = new ArrayList<>();
    int chunkSize = state.numbers.size() / numThreads;

    for (int i = 0; i < numThreads; i++) {
        final int start = i * chunkSize;
        final int end = (i == numThreads - 1) ? state.numbers.size() : (i + 1) * chunkSize;
        tasks.add(() -> {
            int sum = 0;
            for (int j = start; j < end; j++) {
		int number = state.numbers.get(j);
	        if (number % 2 == 0) {
		    sum = sum + (number * number);
	        }
            }
            return sum;
        });
    }

    int totalSum = 0;
    for (Future<Integer> result : executorService.invokeAll(tasks)) {
        totalSum += result.get();
    }

    executorService.shutdown();
    return totalSum;
}

We can use Java’s concurrency utilities, such as ExecutorService, to execute tasks concurrently. We divide the list into chunks and process them concurrently using a thread pool. When deciding between streams and for-loops for parallelism and concurrency, we should consider the complexity of our task. Streams offer a more straightforward way to enable parallel processing for tasks that can be parallelized easily. On the other hand, for-loops, with manual concurrency control, are suitable for more complex scenarios that require custom thread management and coordination.

5. Mutability

Now, let’s explore the aspect of mutability and how it differs between streams and for-loops. Understanding how these handle mutable data is essential for making informed choices.

First and foremost, we need to recognize that streams, by their nature, promote immutability. In the context of streams, elements within a collection are not modified directly. Instead, operations on the stream create new streams or collections as intermediate results:

List<String> fruits = new ArrayList<>(Arrays.asList("apple", "banana", "orange"));
List<String> upperCaseFruits = fruits.stream()
  .map(fruit -> fruit.toUpperCase())
  .collect(Collectors.toList());

In this stream operation, the original list remains unchanged. The map() operation produces a new stream where each fruit is transformed to uppercase, and the collect() operation collects these transformed elements into a new list.

Contrastingly, for-loops can operate on mutable data structures directly:

List<String> fruits = new ArrayList<>(Arrays.asList("apple", "banana", "orange"));
for (int i = 0; i < fruits.size(); i++) {
    fruits.set(i, fruits.get(i).toUpperCase());
}

In this for-loop, we directly modify the original list, replacing every element with its uppercase correspondent. This can be advantageous when we need to modify existing data in place, but it also necessitates careful handling to avoid unintended consequences.

6. Conclusion

Both streams and loops have their strengths and weaknesses. Streams offer a more functional and declarative approach, enhancing code readability and often leading to concise and elegant solutions. On the other hand, loops provide a familiar and explicit control structure, making them suitable for scenarios where precise execution order or mutability control is critical.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

Partner – Microsoft – NPI EA (cat = Baeldung)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Microsoft – NPI EA (cat = Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat = Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag = Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Partner – MongoDB – NPI EA (tag=MongoDB)
announcement - icon

Traditional keyword-based search methods rely on exact word matches, often leading to irrelevant results depending on the user's phrasing.

By comparison, using a vector store allows us to represent the data as vector embeddings, based on meaningful relationships. We can then compare the meaning of the user’s query to the stored content, and retrieve more relevant, context-aware results.

Explore how to build an intelligent chatbot using MongoDB Atlas, Langchain4j and Spring Boot:

>> Building an AI Chatbot in Java With Langchain4j and MongoDB Atlas

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook – Java Streams – NPI (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook Jackson – NPI EA – 3 (cat = Jackson)
4 Comments
Oldest
Newest
Inline Feedbacks
View all comments