Streams in Java: Stream API, Methods, and Operations

Updated on 02/09/20254,355 Views

Streams in Java were introduced in Java 8 to simplify data processing. A stream represents a sequence of objects that can be processed in a functional style. Unlike collections, streams do not store data but provide methods to perform operations such as filtering, mapping, and reducing. This allows developers to work with large datasets more efficiently and with cleaner code. 

This tutorial on Streams in Java covers the key concepts, operations, and methods of the Stream API. You will learn about intermediate and terminal operations, features of streams, and practical examples with code snippets. The blog is designed to help learners understand how to use Java Streams effectively for building robust and maintainable applications. 

Advance your Java programming expertise with our Software Engineering courses and elevate your learning to the next level. 

What are Streams in Java? 

Streams in Java are a feature introduced in Java 8 that allow developers to process collections of data in a functional and declarative manner. A stream represents a sequence of elements that can be transformed, filtered, or reduced using various operations.  

Accelerate your tech career by mastering future-ready skills in Cloud, DevOps, AI, and Full Stack Development. Gain hands-on experience, learn from industry leaders, and develop the expertise that top employers demand. 

  • Professional Certificate Program in Cloud Computing and DevOps
  • AI-Powered Full Stack Development Course by IIITB
  • Future-Proof Your Tech Career with AI-Driven Full-Stack Development

Unlike collections, streams do not store data; they work on data sources such as lists, arrays, or I/O channels. Streams in Java support method chaining, lazy evaluation, and parallel processing, making them powerful for handling large datasets efficiently while keeping code clean, concise, and easy to maintain. 

Different Operations on Streams

Intermediate Operations

Intermediate operations are operations that change one stream into another stream. These operations are called "intermediate" because they do not produce a final result or a terminal operation but instead return a new stream that can be further operated upon. The following are a few common intermediate operations in Java Streams:

  • Map: The map() operation applies a given function to each element in the stream. Then it returns a new stream comprising the results of those function applications. The function takes a single argument of the stream element type. Then it returns a value of any type. The resulting stream will have the same number of elements as the original stream.
  • Filter: The filter() operation takes a Predicate as an argument and returns a new stream. This contains only the elements of the original stream that satisfy the given predicate. The predicate takes a single argument of the stream element type. This returns a boolean value.
  • Sorted: The sorted() operation returns a new stream that contains the same elements as the original stream. But this is in a sorted order. The order is determined by the elements' natural order or by a specified Comparator.

Must Read: Top 13 String Functions in Java

Terminal Operations

Terminal operations in Java streams are those operations that initiate the processing of the stream elements and return a non-stream result. Here are explanations for three common terminal operations:

  • Collect: The collect() method accumulates elements from a stream into a collection, such as a List, Set, or Map. It takes a Collector as an argument. This specifies how to perform the accumulation. The Collector interface provides many built-in methods to perform the collection operation. Alternatively, you can create your own custom collector.
  • ForEach: The forEach() method acts on each stream element. It takes a Consumer as an argument. This specifies the action to perform.
  • Reduce: The reduce() method reduces the stream's elements. It takes a BinaryOperator as an argument. This specifies how to combine two stream elements into a single result. You can also provide an initial value for the reduction.

Program to Demonstrate The Use of Stream

Here is a program to demonstrate the use of Stream with the stream() method:

import java.util.Arrays;
import java.util.List;
public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        int sum = numbers.stream().mapToInt(Integer::intValue).sum();
        System.out.println("Sum: " + sum);
    }
}

In this program, we have a list of integers called numbers that contains the values 1, 2, 3, 4, and 5.

We use the Stream API to create a stream from the numbers list by calling the stream() method. Then, we use the mapToInt() method to convert the stream of Integer objects to an IntStream, which allows us to perform numeric operations. Finally, we call the sum() method on the IntStream to calculate the sum of the numbers in the stream and then print it.

Stream Provides The Following Features

Java Streams provide several features that make it easy to process data collections concisely and efficiently. Here are some of the main features that streams provide:

Parallelism: Streams can be easily parallelized. This means that they can be split into multiple parts. Then, they are processed in parallel across multiple threads or processors. This can enhance performance for large datasets to a great extent.

Lazy Evaluation: As already mentioned, streams use lazy evaluation. This means intermediate operations are not executed until a terminal operation is called on the stream. This allows for more efficient use of resources and can improve performance for complex stream pipelines.

Method Chaining: Streams support method chaining. This allows multiple operations to be chained together into a single stream pipeline. This makes writing concise and readable codes that perform complex data transformations easy.

Non-mutating Operations: Streams provide a set of non-mutating operations that do not modify the original collection. However, these operations return a new stream with the desired changes instead. This can make it easier to reason about the code. It may also avoid unexpected side effects.

Functional Programming: Streams use functional programming concepts, such as higher-order functions and lambda expressions. It makes it easy to write expressive and reusable code.

Support for Different Data Sources: Streams can be created from various data sources, such as collections, arrays, or files. They can be easily converted into other data structures or formats.

Also Read: A Complete Guide to Java Keywords

Java Stream Interface Methods

The Java Stream interface provides several methods that allow you to perform various operations on a stream. Here are some of the Java stream interface methods:

  • filter(Predicate<T> predicate): For returning a new stream containing elements that satisfy the given predicate.
  • map(Function<T, R> mapper): For returning a new stream by applying a given function to each element of the original stream.
  • flatMap(Function<T, Stream<R>> mapper): For returning a new stream by first applying a function to each element of the original stream and then flattening the resulting streams into a single stream.
  • distinct(): For returning a new stream consisting of distinct elements of the original stream.
  • sorted(): For returning a new stream with elements sorted in their natural order.
  • findFirst(): For returning an Optional describing the first element of the stream or an empty Optional if the stream is empty.
  • findAny(): For returning an Optional describing any element of the stream or an empty Optional if the stream is empty.
  • limit(long maxSize): For returning a new stream truncated to a maximum size of maxSize.
  • skip(long n): For returning a new stream that discards the first n elements of the original stream.
  • forEach(Consumer<T> action): For performing an action for each stream element.
  • collect(Collector<T, A, R> collector): For performing a mutable reduction operation on the stream elements using a Collector.
  • reduce(T identity, BinaryOperator<T> accumulator): For performing a reduction on the elements of the stream using the given identity value and the associative accumulation function.
  • anyMatch(Predicate<T> predicate): For returning true if any stream elements match the given predicate; otherwise, returns false.
  • allMatch(Predicate<T> predicate): For returning true if all stream elements match the given predicate; otherwise, returns false.
  • noneMatch(Predicate<T> predicate): For returning true if no stream elements match the given predicate; otherwise, returns false.

Must Read: How to Use the Final Keyword in Java for Variables, Methods, and Classes

Java Stream Example: Filtering Collection by using Stream

Now, let us look at an example of filtering a collection in Java using the Stream API:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
    public static void main(String[] args) {
        // Create a list of strings
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");
        names.add("Eve");
        // Filter the list to get names starting with "A"
        List<String> filteredNames = names.stream()
                .filter(name -> name.startsWith("A"))
                .collect(Collectors.toList());
        // Print the filtered list
        for (String name : filteredNames) {
            System.out.println(name);
        }
    }
}

Java Stream Iterating Example

Here is an example of iterating over a Stream in Java:

import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        // Create a Stream of integers from 1 to 5
        Stream<Integer> stream = Stream.iterate(1, n -> n + 1)
                .limit(5);
        // Iterate over the Stream and print the elements
        stream.forEach(System.out::println);
    }
}

In this example, we create a Stream of integers using the Stream.iterate() method. The iterate() method takes an initial value (1 in this case) and a lambda expression that defines the function to generate the next value based on the previous value (n -> n + 1 in this case). We limit the stream to contain only 5 elements using the limit() method.

Java Stream Example: reduce() Method in Collection

Here is an example of using the reduce() method with Stream in Java on a collection:

import java.util.Arrays;
import java.util.List;
public class Main {
    public static void main(String[] args) {
        // Create a list of integers
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        // Use reduce() to calculate the sum of the numbers
        int sum = numbers.stream()
                .reduce(0, Integer::sum);
        System.out.println("Sum: " + sum);
    }
}

Java Stream Example: Sum by using Collectors Methods

Now, here is an example of using the Collectors method with Stream in Java for also calculating the sum of 1, 2, 3, 4, and 5:

Java Stream Example: Find Max and Min Product Price

Here's an example of using Stream in Java to find the maximum and minimum product prices from a collection:

Java Stream Example: count() Method in Collection

Here is an example of using the count() method with Stream in Java to count the number of elements in a collection:

import java.util.Arrays;
import java.util.List;
public class Main {
    public static void main(String[] args) {
        // Create a list of strings
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");
        // Use count() to get the number of elements
        long count = names.stream().count();
        System.out.println("Count: " + count);
    }
}

Java Stream Example: Convert List into Set

Here is an example of using Stream in Java to convert a list into a set:

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class Main {
    public static void main(String[] args) {
        // Create a list of integers
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        numbers.add(3); // Duplicate element
        // Convert the list to a set using Collectors.toSet()
        Set<Integer> uniqueNumbers = numbers.stream()
                .collect(Collectors.toSet());
        // Print the unique numbers in the set
        for (Integer number : uniqueNumbers) {
            System.out.println(number);
        }
    }
}

Java Stream Example: Convert List into Map

Now, here is an example of using Stream in Java to convert a list into a map:

Conclusion

Streams in Java simplify the way developers handle data by enabling functional-style operations on collections. They provide efficient tools for filtering, mapping, reducing, and transforming data without modifying the original source. With features like lazy evaluation, method chaining, and parallel execution, Streams in Java make code more readable and performance-driven.  

This tutorial explained their core concepts, operations, and examples to help you understand practical applications. Mastering the Stream API equips developers to build scalable, clean, and efficient Java programs, making it an essential concept for anyone learning or working with modern Java development. 

FAQs

1. What is the difference between a stream and a collection in Java?

A collection is a data structure that stores and manages a group of objects. In contrast, a stream is a way to process a group of objects functionally and declaratively.

2. Are Java Streams thread-safe?

No, Java Streams are not thread-safe by default. If a stream is used by multiple threads concurrently, it can lead to race conditions and other concurrency issues.

3. Can Java Streams be infinite?

Yes, Java Streams can be created with an infinite number of elements. This is done without loading all the data into memory at once, allowing for efficient processing of large datasets.

image

Take the Free Quiz on Java

Answer quick questions and assess your Java knowledge

right-top-arrow

FREE COURSES

Start Learning For Free

image
Pavan Vadapalli

Author|911 articles published

Pavan Vadapalli is the Director of Engineering , bringing over 18 years of experience in software engineering, technology leadership, and startup innovation. Holding a B.Tech and an MBA from the India....

image
Join 10M+ Learners & Transform Your Career
Learn on a personalised AI-powered platform that offers best-in-class content, live sessions & mentorship from leading industry experts.
advertise-arrow

Free Courses

Explore Our Free Software Tutorials

Top Resources

Recommended Programs

upGrad Learner Support

Talk to our experts. We are available 7 days a week, 10 AM to 7 PM

text

Indian Nationals

text

Foreign Nationals