Open In App

Java 8 Stream Tutorial

Last Updated : 12 Sep, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Java 8 introduced the Stream API, which allows developers to process collections of data in a functional and declarative way. Streams make it easier to perform operations such as filtering, mapping, reducing and collecting data without writing complex loops.

1. Introduction to Streams

A Stream is a sequence of elements that supports functional-style operations. Unlike Collections, a Stream does not store data it only processes it.

Java-Streams
Stream

Features of Streams

  • Declarative: Write concise and readable code using functional style.
  • Lazy Evaluation: Operations are executed only when needed (terminal operation).
  • Parallel Execution: Supports parallel streams to leverage multi-core processors.
  • Reusable Operations: Supports chaining of operations like map(), filter(), sorted().
  • No Storage: Streams don’t store data; they only process it.

How does Stream Work Internally?

  • Create a Stream: From collections, arrays or static methods.
  • Apply Intermediate Operations: Transform data (e.g., filter(), map(), sorted()).
  • Apply Terminal Operation: Produce a result (e.g., forEach(), collect(), reduce()).

2. Creation of Streams

Streams Creation can be done by multiple ways:

  1. From a Collection: Create a stream directly from a List, Set or any Collection using stream()
  2. From an Array: Use Arrays.stream(array) to convert an array into a stream.
  3. Using Stream.of(): Create a stream from a fixed set of values using Stream.of().
  4. Infinite Stream: Generate an unbounded sequence using Stream.iterate() or Stream.generate()

Example 

Java
import java.util.*;
import java.util.stream.*;

public class StreamCreation {
    public static void main(String[] args) {
        // 1. From a Collection
        List<String> list = Arrays.asList("Java", "Python", "C++");
        Stream<String> stream1 = list.stream();

        // 2. From an Array
        String[] arr = {"A", "B", "C"};
        Stream<String> stream2 = Arrays.stream(arr);

        // 3. Using Stream.of()
        Stream<Integer> stream3 = Stream.of(1, 2, 3, 4, 5);

        // 4. Infinite Stream (limit to avoid infinite loop)
        Stream<Integer> stream4 = Stream.iterate(1, n -> n + 1).limit(5);
        stream4.forEach(System.out::println);
    }
}

Output
1
2
3
4
5

To read more about Creation of Stream -> Streams Creation

3. Stream Pipeline

A Stream Pipeline defines how data flows through different stages. It has three parts:

3.1 Source

The source provides the data for the stream. It can be a collection, array, file or even an infinite generator.

Example:

List<Integer> numbers = Arrays.asList(10, 20, 30, 40);

Stream<Integer> stream = numbers.stream(); // Source

3.2 Intermediate Operations

Intermediate operations transform a stream into another stream. Some common intermediate operations include:

  1. filter(): Filters elements based on a specified condition.
  2. map(): Transforms each element in a stream to another value.
  3. Sorted(): Sorts the elements of a stream.
  4. Distinct(): Remove duplicates.
  5. Skip(): Skip first n elements.

Example:

Java
import java.util.*;
import java.util.stream.*;

public class StreamIntermediate {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(5, 10, 20, 10, 30, 40);

        numbers.stream()
               .filter(n -> n > 10)   // keep > 10
               .map(n -> n * 2)       // double them
               .distinct()            // remove duplicates
               .sorted()              // sort ascending
               .forEach(System.out::println);
    }
}

Output
40
60
80

To read more about Intermediate operations-> Intermediate operations

3.3 Terminal Operations

Terminal Operations are the operations that on execution return a final result as an absolute value.

Example:

Java
import java.util.*;
import java.util.stream.*;

public class StreamTerminal {
    public static void main(String[] args)
    {
        List<String> names = Arrays.asList("Amit", "Riya", "Rohan", "Amit");

        // Collect into Set (removes duplicates)
        Set<String> uniqueNames = names.stream().collect(Collectors.toSet());
        System.out.println(uniqueNames);

        // Count names starting with 'R'
        long count = names.stream().filter(n -> n.startsWith("R")).count();
        System.out.println("Names starting with R: " + count);

        // Reduce (concatenate names)
        String result = names.stream().reduce("", (a, b) -> a + b + " ");
        System.out.println(result);
    }
}

Output
[Amit, Riya, Rohan]
Names starting with R: 2
Amit Riya Rohan Amit 

4. Types of Streams

Streams can be categorized into different types based on their nature and usage.

4.1 Sequential Stream

  • Processes elements one by one in a single thread.
  • Created by default when you call stream().

Example:

Java
import java.util.*;
public class SequentialStreamDemo {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("A", "B", "C", "D");

        names.stream()
             .forEach(System.out::println); // Executes sequentially
    }
}

Output
A
B
C
D

4.2 Parallel Streams

Parallel Streams are the type of streams that can perform operations concurrently on multiple threads. These Streams are meant to make use of multiple processors or cores available to speed us the processing speed. There are two methods to create parallel streams are mentioned below:

  1. Using the parallel() method on a stream
  2. Using parallelStream() on a Collection

Example:

Java
import java.util.*;
import java.util.stream.*;

public class ParallelStreamDemo {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9);

        numbers.parallelStream().forEach(n -> System.out.println(n + " " + Thread.currentThread().getName()));
    }
}

Output
2 ForkJoinPool.commonPool-worker-3
1 ForkJoinPool.commonPool-worker-3
4 ForkJoinPool.commonPool-worker-3
5 ForkJoinPool.commonPool-worker-3
7 ForkJoinPool.commonPool-worker-3
6 main
8 ForkJoinPool.com...

To know more about Parallel Streams -> Parallel Streams

4.3 Infinite Streams

Streams can also generate unbounded sequences. Use limit() to avoid infinite execution.

Example:

Java
import java.util.stream.*;

public class InfiniteStreamDemo {
    public static void main(String[] args) {
        Stream.iterate(1, n -> n + 1)
              .limit(5)
              .forEach(System.out::println);
    }
}

Output
1
2
3
4
5

4.4 Primitive Streams

Java provides specialized streams for primitive data types:

  • IntStream -> for int values
  • LongStream -> for long values
  • DoubleStream -> for double values

Example:

Java
import java.util.stream.IntStream;

public class PrimitiveStreamDemo {
    public static void main(String[] args) {
        IntStream.range(1, 5).forEach(System.out::println);
    }
}

Output
1
2
3
4

6. Stream vs Collection difference

  • Collection stores data in memory and represents a data structure (e.g., List, Set, Map).
  • Stream does not store data; it processes data from a source (like a collection) in a functional, declarative way.

To know more about Stream vs Collection refer-> Stream vs Collection

7. Java Stream: File Operation

In this section, we see how to utilize Java stream in file I/O operation. 

7.1 File Read Operation

Let's understand file read operation through the given example

Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class GFG {
    // Method to filter strings of a given length and convert them to uppercase
    private static List<String>
    filterAndConvertToUpper(Stream<String> stream, int length)
    {
        return stream.filter(s -> s.length() == length)
            .map(String::toUpperCase)
            .collect(Collectors.toList());
    }

    public static void main(String[] args)
    {
        // Replace with the actual file path
        String fileName = "path/to/your/file.txt";

        // Step 1: Create a Stream of lines from the file
        try (Stream<String> lines = Files.lines(Paths.get(fileName))) {

            List<String> filteredStrings = filterAndConvertToUpper(lines, 5);
            System.out.println("Filtered strings with length 5 (converted to uppercase): "+ filteredStrings);
        }
        catch (IOException e) {e.printStackTrace();
        }
    }
}

Input:

Geeks gfg geeks geeksforgeeks Coder Guys

Output:

Filtered strings with length 5 (converted to uppercase): [GEEKS, GEEKS, CODER]

7.2 File Write Operation

Let's understand file write operation through the given example

Java
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

// Driver Class
class GFG {
    // main function
    public static void main(String[] args)
    {
        String[] words
            = { "Geeks", "for", "Geeks", "Hello", "World" };

        // Replace with the actual file path

        String fileName = "path/to/your/file.txt";

        // Step 1: Create a PrintWriter to write to the file
        try (PrintWriter pw
             = new PrintWriter(Files.newBufferedWriter(
                 Paths.get(fileName)))) {

            // Step 2: Use Stream to write each word to the file
            Stream.of(words).forEach(pw::println);

            // Step 3: Print success message to the console
            System.out.println(
                "Words written to the file successfully.");
        }
        catch (IOException e) {
            // Step 4: Handle any IO exception that occurs during the file writing process
            e.printStackTrace();
        }
    }
}

Output:

Words written to the file successfully.

8. Java Stream: Real-life Examples

Streams are widely used in real-world applications for processing collections of data in a cleaner and faster way. Below are some practical examples:

  • Filtering Employees by Salary
  • Streams in a Grocery store
  • Grouping Books by Author

Example: Streams in a Grocery store

Streams in a Grocery store 

Code Implementation: Here we implement a real-world example of filtering, sorting, mapping and collecting transactions using Java Streams.

Java
import java.util.*;
import java.util.stream.Collectors;

class Transaction {
    private int id;
    private int value;
    private String type;

    public Transaction(int id, int value, String type) {
        this.id = id;
        this.value = value;
        this.type = type;
    }

    public int getId() {
        return id;
    }

    public int getValue() {
        return value;
    }

    public String getType() {
        return type;
    }
}

public class StreamExample {
    public static void main(String[] args) {
        List<Transaction> transactions = Arrays.asList(
            new Transaction(1, 100, "GROCERY"),
            new Transaction(3, 80, "GROCERY"),
            new Transaction(6, 120, "GROCERY"),
            new Transaction(7, 40, "ELECTRONICS"),
            new Transaction(10, 50, "GROCERY")
        );

        // Stream pipeline based on your diagram
        List<Integer> transactionIds = transactions.stream()
                .filter(t -> t.getType().equals("GROCERY"))       // keep only groceries
                .sorted(Comparator.comparing(Transaction::getValue).reversed()) // sort by value desc
                .map(Transaction::getId)                         // map to id
                .collect(Collectors.toList());                   // collect as list

        System.out.println(transactionIds); 
    }
}

Output
[6, 1, 3, 10]

Article Tags :