Description

Java’s java.util.stream feature has been around since Java 8. It allows functional-style operations on collections of elements.

Some commonly used functions are:

Common Java Stream Methods

Method Description
.filter() Selects elements that match a given predicate.
.map() Transforms each element using a provided function.
.collect() Create a List<> or Set<> of results.
.sorted() Sorts elements in natural or custom order.
.forEach() Performs an action on each element (usually for side-effects like printing).
.limit() Truncates the stream to a specified number of elements.
.skip() Skips a specified number of elements.
.distinct() Removes duplicate elements based on equals().
.anyMatch() Checks if any element matches a given condition.
.count()
.sum()
.average()
.max()
.min()
Returns the total, sum, average, max or min value.

Commonly used features

Looping over items

items.stream().forEach(x -> {  if (x.getY().toUpper().contains("Z")) { System.out.println(x); }});

Debugging

TIP: Use .peek(System.out::println) between stages to preview items without collecting them.

Top 5 items in a large list

If I have a large (>1000 items in a) List or a Set in a debugger and I want to perform a search and display a small subset of how can I do that? Say match on a specific thing sort the list and display only the first 5 items in the list.

items.stream()
     .filter(item -> item.getName().contains("target")) // 🔍 match condition
     .sorted(Comparator.comparing(Item::getName))       // 🔠 sort condition
     .limit(5)                                           // 📉 reduce to first 5
     .collect(Collectors.toList())                      // 📦 collect results

Top 100th item in a large list

list.stream().skip(99).limit(2).forEach(System.out::println); // 100 & 101 element

Filtering - searching

List<String> list = Arrays.asList("apple", "apple", "banana", "cherry");
Stream<String> stream = list.stream();
List<String> filteredList = list.stream()
                                .filter(s -> s.startsWith("a"))
                                .collect(Collectors.toList());
// => ["apple", "apple"]

List<String> longWords = list.stream()
                             .filter(s -> s.length() > 5)
                             .collect(Collectors.toList());
// => [ "banana", "cherry" ]

Set<String> longWords = list.stream()
                             .filter(s -> s.length() > 5)
                             .collect(Collectors.toSet());
// => { "banana", "cherry" }

Sum of all objects

In Java when you traverse a collection of items and create a single result that is called Reducing. On example, is getting the sum or average of all of the items in a list.

List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50);
int sum = numbers.stream()
    .reduce(0, Integer::sum);

double average = numbers.stream()
                        .mapToInt(Integer::intValue)
                        .average()
                        .orElse(0.0);  // default if the list is empty

Another example of getting the average

import java.util.OptionalDouble;
import java.util.stream.IntStream;

public class AverageExample {
    public static void main(String[] args) {
        IntStream intStream = IntStream.of(2, 4, 6, 8, 10);
        OptionalDouble average = intStream.average();

        if (average.isPresent()) {
            System.out.println("Average: " + average.getAsDouble());
        } else {
            System.out.println("Stream is empty");
        }
    }
}

Mapping - Converting one list of objects to another

Convert the elements from one thing to another (aka mapping).

Convert a list of strings to be uppercase.

List<String> list = Arrays.asList("apple", "banana", "cherry");
List<String> uppercasedList = list.stream()
                                  .map(String::toUpperCase)
                                  .collect(Collectors.toList());

Convert one kind of object Boat to another kind (e.g., Vehicle).

class Book {
    private String title;
    private String author;
    private String isbn;

    public Book(String title, String author, String isbn) {
        this.title = title;
        this.author = author;
        this.isbn = isbn;
    }

    public String getTitle() { return title; }
    public String getAuthor() { return author; }
    public String getIsbn() { return isbn; }
}

class DisplayCard {
    private String heading;
    private String subtext;

    public DisplayCard(String heading, String subtext) {
        this.heading = heading;
        this.subtext = subtext;
    }

    @Override
    public String toString() {
        return "DisplayCard{heading='" + heading + "', subtext='" + subtext + "'}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        DisplayCard that = (DisplayCard) o;
        return Objects.equals(heading, that.heading) && Objects.equals(subtext, that.subtext);
    }

    @Override
    public int hashCode() {
        return Objects.hash(heading, subtext);
    }
}

List<Book> books = Arrays.asList(
    new Book("Effective Java", "Joshua Bloch", "9780134685991"),
    new Book("Clean Code", "Robert C. Martin", "9780132350884"),
    new Book("Effective Java", "Joshua Bloch", "9780134685991") // duplicate
);

// Convert list of books to a set of display cards
Set<DisplayCard> cards = books.stream()
    .map(book -> new DisplayCard(book.getTitle(), "by " + book.getAuthor()))
    .collect(Collectors.toSet());
cards.forEach(System.out::println);
// => sample output is
// DisplayCard{heading='Effective Java', subtext='by Joshua Bloch'}
// DisplayCard{heading='Clean Code', subtext='by Robert C. Martin'}

Creating a List from a Set of objects

NOTE: Duplicate Vehicle objects only if the Vehicle class properly overrides equals() and hashCode(). If not, all objects will be treated as unique even if the content is the same.

Stream.of("a", "b", "c", "a")
    .distinct()
    .forEach(System.out.println);

Some gotcha’s

Use default sorted() with care

https://stackoverflow.com/questions/53219523/why-is-stream-sorted-not-type-safe-in-java-8

class Foo {
    public static void main(String[] args) {
        Arrays.asList(new Foo(), new Foo()).stream().sorted().forEach(f -> {});
    }
}

Will compile just fine but will throw an exception at run time:

Exception in thread "main" java.lang.ClassCastException: Foo cannot be cast to java.lang.Comparable

And

public class Foo implements Comparable<Foo> { /* implementation */ }
public class Bar extends Foo {}
public class Qux extends Foo {}
Stream<Bar> stream = barCollection.stream().sorted(Qux.class);

Compiles but won’t work. Since both Bar and Qux match Comparable<? super Foo> there is no compile-time error and thus no type-safety is added. Also, the implication of requiring a Class argument is that it’ll be used for casting. At runtime this can, as shown above, still result in ClassCastExceptions. If the Class isn’t used for casting then the argument is completely useless.


<
Previous Post
How can I install Node using nvm?
>
Next Post
Disable google login popup