Java 8 Stream API Tutorial | Stream API

Java 8 Features with example

What is Stream API in Java ?

Java 8 Stream API Tutorial

  • A Steam is a sequence of elements that supports various methods for processing data in parallel or sequentially
  • A Stream is not a data structure; it doesn’t store elements.
  • Streams can be created from collection, array, or I/O source
  • Using Java 8 Stream API You can chain multiple operations together

Key features of Stream API ?

  • Supports Functional Programming (Lambda expressions)
  • Performs operations like filtering, mapping, and reducing
  • Supports parallel processing for better performance
  • Does NOT modify the original data (immutable)
  • Uses lazy evaluation (processes elements only when needed)

Types of Steams

There are two types of Streams in Java 8.

  • Sequential Stream: A stream that processes elements one at a time in a sequence.

  • Parallel Stream: A stream that processes elements concurrently in parallel (e.g., by utilizing multiple CPU cores).

Learn more about other Java 8 features.

How to create a Stream in Java 8

Java 8 Stream API is available in java.util.stream package. There are multiple ways to create streams in Java 8:

  • From collections (using stream() method)

  • From arrays (using Arrays.stream())

  • Using Stream.of() for specific values

  • From files (using Files.lines())

  • Generating infinite streams using Stream.generate() and Stream.iterate()

  • Using parallelStream() for parallel processing

Lets look at how to create a Java Stream by coding examples 
 

Creating a Stream using collection

We can create a stream from any collection class (like List, Set, etc.) using the stream() method. Below is an example of creating a stream from a list. Here, we are also using the map function to transform each element of the list to upper case.


//Example: Creating a Stream from a List
import java.util.*;
import java.util.stream.*;

public class StreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
        // Creating a stream from a list
        Stream<String> nameStream = names.stream();
        // Processing: Convert names to uppercase
        List<String> uppercaseNames = nameStream.map(String::toUpperCase)
                                                .collect(Collectors.toList());
        System.out.println(uppercaseNames); // [ALICE, BOB, CHARLIE]
    }
}
✔ Streams do not modify the original collection 
✔ map() applies a transformation to each element

Creating a Stream from an Array

We can create a stream from an array usingArrays.stream() method. example below shows creation of a stream from integer arrays and arrays of objects


//Example: Creating a Stream from integer arrays and arrays of object
import java.util.Arrays;

public class StreamFromArray {
    public static void main(String[] args) {
        // Creating a stream from an array of primitive type
        int[] numbers = {1, 2, 3, 4, 5};
        Arrays.stream(numbers)
              .filter(n -> n % 2 == 0)
              .forEach(System.out::println);  // Output: 2, 4
              
        //creating a stream fron array of objects 
        String[] names = {"John", "Alice", "Bob"};
        // Creating a stream from an array of objects
        Arrays.stream(names)
              .filter(name -> name.length() > 3)
              .forEach(System.out::println);  // Output: Alice
    }
}

Creating a Stream Using Stream.of()

The Stream.of() method creates a stream from a sequence of values.


//Example: Using Stream.of()
import java.util.stream.Stream;

public class StreamFromStreamOf {
    public static void main(String[] args) {
        // Creating a stream from specific values
        Stream<String> namesStream = Stream.of("John", "Alice", "Bob");

        namesStream.filter(name -> name.startsWith("J"))
                   .forEach(System.out::println);  // Output: John
    }
}

Creating a Stream from a File (I/O Streams)

We can create a stream from a file using Files.lines(), which returns a stream of lines from a file.


//Example: From a File
import java.nio.file.*;
import java.io.IOException;

public class StreamFromFile {
    public static void main(String[] args) {
        try {
            Files.lines(Paths.get("sample.txt"))
                 .filter(line -> line.contains("Java"))
                 .forEach(System.out::println);  // Output: Lines containing the word "Java"
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Creating a Stream Using Stream.generate()

Using Stream.generate() method, we can create an infinite stream by repeatedly applying a provided supplier function


//Example: Generating an Infinite Stream of Random Numbers
import java.util.stream.Stream;

public class StreamGenerate {
    public static void main(String[] args) {
        // Creating an infinite stream of random numbers
        Stream<Double> randomNumbers = Stream.generate(Math::random);
        // Limiting the stream to the first 5 elements
        randomNumbers.limit(5).forEach(System.out::println);
    }
}

Creating a Stream Using Stream.iterate()

UsingStream.iterate(),we can create an infinite stream where each element is computed based on the previous one. we can limit the stream to a specific number of elements using limit().


//xample: Iterating Over Numbers
import java.util.stream.Stream;

public class StreamIterate {
    public static void main(String[] args) {
        // Creating a stream of even numbers starting from 0
        Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2);

        // Limiting the stream to the first 5 even numbers
        evenNumbers.limit(5)
                   .forEach(System.out::println);  // Output: 0, 2, 4, 6, 8
    }
}

Creating a Parallel Stream in Java 8

We can create a parallel stream by calling the parallelStream() method on a collection. In Parallel stream each element might be processed in parallel, so we may see different threads processing each element.


//xample: Parallel Stream from a List
import java.util.Arrays;
import java.util.List;

public class ParallelStreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Alice", "Bob", "Charlie");

        // Creating a parallel stream
        names.parallelStream()
             .filter(name -> name.length() > 3)
             .forEach(name -> System.out.println(Thread.currentThread().getName() + " - " + name));
    }
}

Creating an Empty Stream

We can also create an empty stream using empty() method.


Stream<String> emptyStream = Stream.empty();


Java 8 Stream API Operations | Stream API Methods

There are two types of operations are there in Stream API, intermediate operations and terminal operation 

  • Intermediate Operations: These are operations that transform a stream into another stream. (e.g., filter, map, sorted methods). These methods returns a new object of Stream so that you can chain the multiple operations on a stream which is call as Stream pipeline . They are lazy in nature that means they we be invoked once the terminal operation is called.
  • Terminal Operations: These are operations that produce a result or a side effect (e.g., collect, forEach, reduce methods). They terminate the streams so that no further operation is allowed. They are eager and trigger the processing of the stream.  Java 8 download
Lets understand by using examples 

Intermediate Operations

filter() method

filter() method is used to filter the elements based on some condition. Below is the example to demonstrate how filter works. Here from the list of integers we want to filter even numbers.


List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
  List<Integer> evenNumbers = numbers.stream()
                                   .filter(n -> n % 2 == 0)
                                   .collect(Collectors.toList());
  System.out.println(evenNumbers); // Output: [2, 4]


map() method

The map() method is used to transform the elements of a stream. In simple words, if we want to perform some operation on the elements of a stream, then we can use a map.


List<String> words = Arrays.asList("apple", "banana", "cherry");
List<Integer> wordLengths = words.stream()
                                 .map(String::length)
                                 .collect(Collectors.toList());
System.out.println(wordLengths); // Output: [5, 6, 6]

sorted() method

The sorted() method is used to sort the elements of a stream.


List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
List<Integer> sortedNumbers = numbers.stream()
                                     .sorted()
                                     .collect(Collectors.toList());
System.out.println(sortedNumbers); // Output: [1, 2, 3, 4, 5]


Terminal Operations

collect() method

collect method is used to collect the elements of a stream into a collection like a List, Set, or Map. This method always returns a new collection.


List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> collectedNumbers = numbers.stream()
                                        .filter(n -> n % 2 == 0)
                                        .collect(Collectors.toList());
System.out.println(collectedNumbers); // Output: [2, 4]

forEach() method

forEach() method is used to iterate and print the elements of a Stream


List<String> words = Arrays.asList("apple", "banana", "cherry");
 words.stream().forEach(System.out::println);

reduce() method

reduce() method reduces the stream to a single value by combining elements.


List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()reduce(0, (a, b) -> a + b);
System.out.println(sum); // Output: 15


count() method

count() method gives the number of the elements in a stream. It returns a long value.


List<String> words = Arrays.asList("apple", "banana", "cherry");
long count = words.stream()
                  .filter(word -> word.length() > 5)
                  .count();
System.out.println(count); // Output: 2