Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Learn Principles | Fundamentals and Functional Capabilities 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
Principles

The principles of working with the Stream API are based on key concepts of functional programming and lazy evaluation. Let's take a look at the main principles of the Stream API.

Lazy Evaluation

One of the core principles of the Stream API is lazy evaluation.

This means that intermediate operations like filter() or map() are not executed immediately. They simply form a chain of actions that will be executed only when a terminal operation, such as collect() or forEach(), is invoked.

java

Main

copy
12345678910111213
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); long count = names.stream() .filter(name -> name.startsWith("A")) // Forming the operation .count(); // Triggering execution System.out.println(count); // Output: 1 } }

This code creates a stream from a list of strings, filters the elements to keep only those starting with the letter A, and counts how many meet the condition. The result (1) is printed since only Alice satisfies the condition.

Functional Programming Style

The Stream API utilizes lambda expressions and functional interfaces for data processing. Instead of the imperative approach with loops, you describe what needs to be done with the data:

java

Main

copy
1234567891011121314
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> fruits = List.of("apple", "banana", "cherry", "apricot", "blueberry"); List<String> result = fruits.stream() .map(String::toUpperCase) // Convert all strings to uppercase .skip(3) .toList(); // Collect the result into a new list System.out.println(result); } }

This code creates a stream from a list of fruits, converts the strings to uppercase using map(), and skips the first three elements with skip(). The result is collected into a new list and printed, starting from the fourth element.

Immutability of Data

The Stream API does not modify the original data. All operations create a new stream or return a result without modifying the collection or array. This enhances data safety and prevents unexpected side effects.

java

Main

copy
12345678910111213
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); List<String> updateNames = names.stream() .filter(name -> name.startsWith("A")) .toList(); System.out.println(names); // Output: [Alice, Bob, Charlie] } }

The Stream API filters the list names to create a new stream, updateNames, that contains only elements starting with the letter A. However, the original list names remains unchanged, as the Stream API does not modify the data directly, ensuring data safety and preventing side effects.

Streams Are Consumable Once

A stream can be used only once. After a terminal operation is performed, the stream becomes unavailable for further processing.

java

Main

copy
123456789101112
package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream<String> stream = List.of("Alice", "Bob").stream(); stream.forEach(System.out::println); stream.forEach(System.out::println); // Error: Stream has already been used } }

Here, we create a stream from a list of strings and print each element to the console. After the stream is used for the first time, it cannot be reused, which causes an error if you attempt to call forEach() again.

Parallel Processing

The Stream API supports parallel streams, enabling faster data processing on multi-core systems. This is explored in more detail here

java

Main

copy
1234567891011
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5); numbers.parallelStream() .forEach(System.out::println); // Elements are processed in parallel } }

This code creates a parallel stream from a list of numbers and prints each element to the console. Using parallelStream() allows for parallel processing of the list elements, which can speed up execution when dealing with large datasets.

Code Cleanliness and Readability

Using the Stream API makes the code more declarative. Instead of describing how to perform a task (like using loops), you describe what exactly needs to be done. This enhances readability and simplifies code maintenance.

Example with a loop:

Example using Stream API:

In the first loop example, we explicitly describe how to iterate through the list and print the elements, while in the second example with the Stream API, we simply specify what needs to be done: iterate through the elements and print them. This makes the code more declarative and readable.

Everything was clear?

How can we improve it?

Thanks for your feedback!

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