Java 8 Stream API Methods

Java 8 Features with example

Java 8 Stream API Methods revolutionized the way Java developers process collections of data. Introduced in Java 8, the Stream API enables functional-style operations on sequences of elements, such as filtering, mapping, and reducing. It allows concise, readable, and efficient code that performs aggregate operations on data.

In this article, we’ll explore the most commonly used Java 8 Stream API methods, along with easy-to-understand coding examples to help you master functional programming in Java.

What is the Java 8 Stream API?

The Stream API is a part of the java.util.stream package. A Stream is not a data structure; instead, it represents a sequence of elements from a source, such as a collection or array, that supports aggregate operations.

Key Characteristics of Streams :

  • No storage: It doesn’t store elements.

  • Functional in nature: Uses functional interfaces and lambda expressions.

  • Lazily evaluated: Operations are not executed until a terminal operation is invoked.

  • Can be parallelized: Use .parallelStream() to improve performance.

Types of Stream Operations

There are two main types of operations in the Stream API:

  1. Intermediate operations – Return a new stream and are lazy.

  2. Terminal operations – Produce a result or a side-effect and trigger processing.

Let’s break down all the commonly used Java 8 Stream API methods under each category.

Intermediate Java 8 Stream API Methods

All These methods return another Stream object, allowing chaining of operations.

filter(Predicate predicate)

Filters elements based on a condition.


list.stream()
    .filter(s -> s.startsWith("A"))
    .collect(Collectors.toList());


map(Function mapper)

map method Transforms each element using a function.java


List list = Arrays.asList("Apple","Orange");
list.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());
    //output : APPLE, ORANGE

flatMap(Function> mapper)- what is flatmap in Java

flatmap in Java 8. It helps you flatten nested structures like List> into a single Stream.It is often used when each element can map to multiple values, and you want to handle them in a single stream.


//Flattening a List of Lists
import java.util.*;
import java.util.stream.*;

public class FlatMapExample {
    public static void main(String[] args) {
        List<list>&gt; listOfLists = Arrays.asList(
            Arrays.asList("a", "b"),
            Arrays.asList("c", "d"),
            Arrays.asList("e", "f")
        );

        List<string> flattenedList = listOfLists.stream()
            .flatMap(List::stream)
            .collect(Collectors.toList());

        System.out.println(flattenedList); // [a, b, c, d, e, f]
    }
}
</string></list>

distinct()

It removes duplicate elements from a stream based on equals() and hashCode().


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

public class DistinctExample {
    public static void main(String[] args) {
        List<integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);

        List<integer> distinctNumbers = numbers.stream()
            .distinct()
            .collect(Collectors.toList());

        System.out.println(distinctNumbers); // [1, 2, 3, 4, 5]
    }
}</integer></integer>

sorted() or sorted(Comparator comparator)

The sorted() method in Java 8 Streams is used to sort elements. It works with natural order (like numbers or strings), or with a custom comparator.


//sorted example with natural order and custom Comparator
import java.util.*;
import java.util.stream.*;

class Person {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}

public class AllSortedExamples {
    public static void main(String[] args) {

        // 1. Sorting Integers (Natural Order)
        List<integer> numbers = Arrays.asList(5, 3, 8, 1, 4);
        List<integer> sortedNumbers = numbers.stream()
            .sorted()
            .collect(Collectors.toList());
        System.out.println("Sorted Integers (Asc): " + sortedNumbers);

        // 2. Sorting Integers (Descending Order)
        List<integer> sortedNumbersDesc = numbers.stream()
            .sorted(Comparator.reverseOrder())
            .collect(Collectors.toList());
        System.out.println("Sorted Integers (Desc): " + sortedNumbersDesc);

        // 3. Sorting Strings Alphabetically
        List<string> names = Arrays.asList("Charlie", "Alice", "Bob");
        List<string> sortedNames = names.stream()
            .sorted()
            .collect(Collectors.toList());
        System.out.println("Sorted Names: " + sortedNames);

        // 4. Sorting Custom Objects by Age
        List<person> people = Arrays.asList(
            new Person("Alice", 30),
            new Person("Bob", 25),
            new Person("Charlie", 35),
            new Person("Bob", 28)
        );

        List<person> sortedByAge = people.stream()
            .sorted(Comparator.comparingInt(p -&gt; p.age))
            .collect(Collectors.toList());
        System.out.println("People Sorted by Age: " + sortedByAge);

        // 5. Sorting Custom Objects by Name then Age
        List<person> sortedByNameThenAge = people.stream()
            .sorted(Comparator.comparing((Person p) -&gt; p.name)
                              .thenComparingInt(p -&gt; p.age))
            .collect(Collectors.toList());
        System.out.println("People Sorted by Name then Age: " + sortedByNameThenAge);
    }
}
output :
Sorted Integers (Asc): [1, 3, 4, 5, 8]
Sorted Integers (Desc): [8, 5, 4, 3, 1]
Sorted Names: [Alice, Bob, Charlie]
People Sorted by Age: [Bob (25), Bob (28), Alice (30), Charlie (35)]
People Sorted by Name then Age: [Alice (30), Bob (25), Bob (28), Charlie (35)]
</person></person></person></string></string></integer></integer></integer>

peek(Consumer action)

The peek() method in Java 8 Streams is a debugging or intermediate operation that lets you “peek” at the elements in the stream as they flow through the pipeline. It returns a stream with the same elements, but allows you to perform a side-effect operation (like System.out.println).


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

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

        List<string> uppercased = names.stream()
            .peek(name -&gt; System.out.println("Original: " + name))
            .map(String::toUpperCase)
            .peek(name -&gt; System.out.println("Uppercased: " + name))
            .collect(Collectors.toList());

        System.out.println("Final List: " + uppercased);
    }
}
output : 
Original: Alice
Uppercased: ALICE
Original: Bob
Uppercased: BOB
Original: Charlie
Uppercased: CHARLIE
Final List: [ALICE, BOB, CHARLIE]</string></string>

limit(long maxSize)

It is used to truncate a stream — it returns only the first N elements of the stream.


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

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

        List<integer> limited = numbers.stream()
            .limit(3)
            .collect(Collectors.toList());

        System.out.println("Limited to first 3: " + limited); // [10, 20, 30]
    }
}</integer></integer>

skip(long n)

The skip() method in Java 8 is used to skip the first N elements of a stream. It’s often used along with limit() for things like pagination.


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

public class SkipExample {
    public static void main(String[] args) {
        List<string> items = Arrays.asList("A", "B", "C", "D", "E");

        List<string> skipped = items.stream()
            .skip(2)
            .collect(Collectors.toList());

        System.out.println("After skipping 2: " + skipped); // [C, D, E]
    }
}</string></string>

Terminal Stream API Methods

collect(Collector collector)

The collect() method in Java 8 is a terminal operation that transforms a stream into a collection, string, map, or other result container. It’s often used with Collectors utility methods.

Common use case with Collectors :

  • toList()

  • toSet()

  • toMap()

  • joining()

  • groupingBy()

  • partitioningBy()

below is the example to show case the use of collect method in various scenario. In below example we have a class Person with name,city and age.

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

public class CollectExample {
    public static void main(String[] args) {
        //Collect to List
        List<string> names = Arrays.asList("Alice", "Bob", "Charlie");
        List<string> collectedList = names.stream()
            .collect(Collectors.toList());
        System.out.println("List: " + collectedList); // [Alice, Bob, Charlie]

        //Collect to Set (removes duplicates)
List<string> items = Arrays.asList("apple", "banana", "apple", "orange");
Set<string> uniqueItems = items.stream()
.collect(Collectors.toSet());
System.out.println("Set: " + uniqueItems); // [banana, orange, apple]

//Collect to Map
List<string> words = Arrays.asList("a", "bb", "ccc");
Map<string integer> wordLengthMap = words.stream()
.collect(Collectors.toMap(word -&gt; word, word -&gt; word.length()));
System.out.println("Map: " + wordLengthMap); // {a=1, bb=2, ccc=3}

//Joining Strings
List<string> names = Arrays.asList("Java", "Python", "C++");
String joined = names.stream()
.collect(Collectors.joining(", "));
System.out.println("Joined: " + joined); // Java, Python, C++

//Grouping By
List<string> words = Arrays.asList("apple", "banana", "cherry", "apricot");
Map<character list>&gt; groupedByFirstChar = words.stream()
.collect(Collectors.groupingBy(word -&gt; word.charAt(0)));
System.out.println("Grouped: " + groupedByFirstChar); 
// {a=[apple, apricot], b=[banana], c=[cherry]}

//Partitioning By
List<integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
Map<boolean list>&gt; evenOddPartition = numbers.stream()
.collect(Collectors.partitioningBy(n -&gt; n % 2 == 0));
System.out.println("Partitioned: " + evenOddPartition);
// {false=[1, 3, 5], true=[2, 4, 6]}   
    }
}</boolean></integer></character></string></string></string></string></string></string></string></string>

Collectors.reducing() – Custom Reduction Logic

Collectors.reducing() is a terminal collector in Java’s Streams API, used to perform reduction operations—combining stream elements into a single result, like summing numbers, finding a maximum, or concatenating strings.

Java 8 Program to Find the Longest Word in the List

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

public class ReducingExample {
    public static void main(String[] args) {
        List<string> words = Arrays.asList("apple", "banana", "cherry", "kiwi");

        String longestWord = words.stream()
            .collect(Collectors.reducing((s1, s2) -&gt; s1.length() &gt; s2.length() ? s1 : s2))
            .orElse("No words");

        System.out.println("Longest word: " + longestWord); // banana
    }
}
</string>

Java 8 Program to Find the Length of the String using reducing ()

List<string> words = Arrays.asList("apple", "banana", "cherry", "kiwi"); 
 int totalLength = words.stream()
    .collect(Collectors.reducing(0, String::length, Integer::sum));

System.out.println("Total length: " + totalLength); // e.g. 22</string>

Collectors.mapping() – Map While Collecting

Collectors.mapping() is super handy when you want to transform (map) elements while collecting them—think of it as a map() + collect() combo in one clean step.

Java 8 Program to group the string by first letter and collect length

List<string> words = Arrays.asList("apple", "banana", "cherry", "kiwi"); 
Map<character list>&gt; groupedLengths = words.stream()
    .collect(Collectors.groupingBy(
        word -&gt; word.charAt(0), // key: first character
        Collectors.mapping(String::length, Collectors.toList()) // map to word length
    ));

System.out.println(groupedLengths);
// {a=[5], b=[6], c=[6], k=[4]}</character></string>

Java 8 Program to group the string by first letter and join all words

List<string> words = Arrays.asList("apple", "banana", "cherry", "kiwi"); 
Map<character string> groupedJoined = words.stream()
    .collect(Collectors.groupingBy(
        word -&gt; word.charAt(0),
        Collectors.mapping(w -&gt; w, Collectors.joining(", "))
    ));

System.out.println(groupedJoined);
// {a=apple, b=banana, c=cherry, k=kiwi}
</character></string>

Real world example of using collect() operations like reducing(), mapping(), and groupingBy().​

Use Case: Products in a Store.

We’ll work with a Product class that has:
name (String), category (String), price (double)

Then we’ll:

  • Find the most expensive product.
  • Calculate total price.
  • Group products by category and map to prices.
  • Group products by category and join names.
  • Get the max price per category

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

class Product {
    String name;
    String category;
    double price;

    Product(String name, String category, double price) {
        this.name = name;
        this.category = category;
        this.price = price;
    }

    @Override
    public String toString() {
        return name + " ($" + price + ")";
    }
}

public class ProductStreamExample {
    public static void main(String[] args) {
        List<product> products = Arrays.asList(
            new Product("Laptop", "Electronics", 1200.00),
            new Product("Phone", "Electronics", 800.00),
            new Product("TV", "Electronics", 1500.00),
            new Product("Sofa", "Furniture", 700.00),
            new Product("Chair", "Furniture", 150.00),
            new Product("Desk", "Furniture", 300.00),
            new Product("Book", "Stationery", 20.00),
            new Product("Pen", "Stationery", 5.00)
        );

        // 1. Reducing: Find the most expensive product
        Product mostExpensive = products.stream()
            .collect(Collectors.reducing((p1, p2) -&gt; p1.price &gt; p2.price ? p1 : p2))
            .orElse(null);
        System.out.println("1. Most Expensive Product: " + mostExpensive);

        // 2. Reducing: Total price of all products
        double totalPrice = products.stream()
            .collect(Collectors.reducing(0.0, p -&gt; p.price, Double::sum));
        System.out.println("2. Total Price: $" + totalPrice);

        // 3. Mapping: Group by category, collect prices
        Map<string list>&gt; pricesByCategory = products.stream()
            .collect(Collectors.groupingBy(
                p -&gt; p.category,
                Collectors.mapping(p -&gt; p.price, Collectors.toList())
            ));
        System.out.println("3. Prices by Category: " + pricesByCategory);

        // 4. Mapping: Group by category, join product names
        Map<string string> namesByCategory = products.stream()
            .collect(Collectors.groupingBy(
                p -&gt; p.category,
                Collectors.mapping(p -&gt; p.name, Collectors.joining(", "))
            ));
        System.out.println("4. Product Names by Category: " + namesByCategory);

        // 5. Mapping + Reducing: Group by category, get max price
        Map<string double> maxPriceByCategory = products.stream()
            .collect(Collectors.groupingBy(
                p -&gt; p.category,
                Collectors.mapping(p -&gt; p.price, Collectors.reducing(0.0, Double::max))
            ));
        System.out.println("5. Max Price by Category: " + maxPriceByCategory);
    }
}
output : 
1. Most Expensive Product: TV ($1500.0)
2. Total Price: $4675.0
3. Prices by Category: {Electronics=[1200.0, 800.0, 1500.0], Furniture=[700.0, 150.0, 300.0], Stationery=[20.0, 5.0]}
4. Product Names by Category: {Electronics=Laptop, Phone, TV, Furniture=Sofa, Chair, Desk, Stationery=Book, Pen}
5. Max Price by Category: {Electronics=1500.0, Furniture=700.0, Stationery=20.0}</string></string></string></product>

forEach method

It acts aseach element in the stream. Usually used for printing, logging, or updating external systems.It’s a terminal operation


List<string> names = Arrays.asList("Alice", "Bob", "Charlie");

names.stream()
    .forEach(name -&gt; System.out.println("Hello, " + name));

output :
Hello, Alice
Hello, Bob
Hello, Charlie</string>

toArray() method in Java 8

It is used to convert a Stream into an array. Below is the example to convert and string stream into String array, we can use any specific type for arrays like integers, long etc.


List<string> names = Arrays.asList("Alice", "Bob", "Charlie");
String[] nameArray = names.stream()
    .toArray(String[]::new);

System.out.println(Arrays.toString(nameArray)); 
// Output: [Alice, Bob, Charlie]
</string>

reduce(BinaryOperator accumulator) method

It reduces the stream’s elements using an associative accumulation function and returns an Optional. accumulator: A function that takes two parameters of type T and returns a value of type T. It combines two elements into one.


//sum of integers from a list of integers
import java.util.Arrays;
import java.util.Optional;

public class ReduceExample {
    public static void main(String[] args) {
        Optional<integer> sum = Arrays.asList(1, 2, 3, 4, 5)
            .stream()
            .reduce((a, b) -&gt; a + b);
        
        sum.ifPresent(System.out::println); // Output: 15
    }
}
</integer>

count() method

Counts the number of elements in a Stream. It return a long value.


long count = list.stream().count();


anyMatch(), allMatch(), noneMatch() methods

  • anyMatch(Predicate predicate) – Returns true if any element matches the predicate
  • allMatch(Predicate predicate) – Returns true if all elements match the predicate
  • noneMatch(Predicate predicate) – Returns true if none of the elements match the predicate

//Example to show anyMatch,allMatch and noneMatch methods
import java.util.Arrays;
import java.util.List;

public class MatchExamples {
    public static void main(String[] args) {
        List<integer> numbers = Arrays.asList(2, 4, 6, 8, 10);

        // anyMatch: checks if *any* element is odd
        boolean anyOdd = numbers.stream().anyMatch(n -&gt; n % 2 != 0);
        System.out.println("Any odd numbers? " + anyOdd); // false

        // allMatch: checks if *all* elements are even
        boolean allEven = numbers.stream().allMatch(n -&gt; n % 2 == 0);
        System.out.println("All numbers even? " + allEven); // true

        // noneMatch: checks if *no* element is negative
        boolean noneNegative = numbers.stream().noneMatch(n -&gt; n &lt; 0);
        System.out.println("No negative numbers? " + noneNegative); // true
    }
}
</integer>

findFirst() and findAny() methods

Both are terminal operations in Java’s Stream API and return an element wrapped in an Optional.

  • fiindFirst() – Returns the first element of the stream, if present.
  • findAny() – Returns any element, useful for performance in parallel streams.

//findFirst,findAny method example
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class FindExamples {
    public static void main(String[] args) {
        List<string> fruits = Arrays.asList("Apple", "Banana", "Mango", "Orange");

        // findFirst: gets the first element in the stream
        Optional<string> firstFruit = fruits.stream().findFirst();
        firstFruit.ifPresent(fruit -&gt; System.out.println("First fruit: " + fruit));

        // findAny: may return any element (especially useful in parallel streams)
        Optional<string> anyFruit = fruits.stream().findAny();
        anyFruit.ifPresent(fruit -&gt; System.out.println("Any fruit: " + fruit));
    }
}
</string></string></string>

FAQs about Java 8 Stream API Methods

Q1. Are streams reusable?
No, Java streams cannot be reused once consumed. You must create a new stream to reprocess data.

Q2. What’s the difference between map and flatMap?
map() transforms elements; flatMap() flattens nested structures (e.g., list of lists into a single list).

Q3. Can I use streams with files or IO operations?
Yes, classes like Files.lines() allow stream processing over file lines.

Q4. When should I use parallelStream()?
A: Use it when processing large datasets where operations are independent and thread-safe.

Conclusion

The Java 8 Stream API methods bring powerful data processing capabilities to Java developers. By learning how to use both intermediate and terminal operations, you can write clean, efficient, and maintainable code. Whether you’re filtering data, mapping values, or collecting results, the Stream API allows you to work with data at a higher abstraction level.With this complete overview of Java 8 Stream API methods, you now have the knowledge to implement modern Java features effectively in your projects.

1 thought on “Java 8 Stream API Methods”

  1. Pingback: Java 8 Features | Java 8 Features with Example - Java Cody

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top