You have a list of orders. You need orders over $100, sorted by date, mapped to customer names. Loops work but are verbose. Streams express this as a pipeline: orders.stream().filter(...).sorted(...).map(...).

Stream basics

Create and use streams.

Basics.java
Replay: real traced execution (multi-file project)
import java.util.*;
import java.util.stream.*;

public class Basics {
    public static void main(String[] args) {
        List<String> raw = Arrays.asList("  Alice ", " Bob", "CHARLIE  ", "  ");

        List<String> names = raw.stream()
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .map(String::toLowerCase)
                .collect(Collectors.toList());

        System.out.println("raw:   " + raw);
        System.out.println("names: " + names);

    }
}
  1. raw ← [ Alice , Bob, CHARLIE , ], names ← [alice, bob, charlie]

    4public class Basics {5    public static void main(String[] args) {6        List<String> raw→ [  Alice ,  Bob, CHARLIE  ,   ] = Arrays.asList("  Alice ", " Bob", "CHARLIE  ", "  ");78        List<String> names→ [alice, bob, charlie] = raw.stream()9                .map(String::trim)10                .filter(s -> !s.isEmpty())11                .map(String::toLowerCase)12                .collect(Collectors.toList());1314        System.out.println("raw:   " + raw[  Alice ,  Bob, CHARLIE  ,   ]);15        System.out.println("names: " + names[alice, bob, charlie]);
    outputraw:   [  Alice ,  Bob, CHARLIE  ,   ]
    names: [alice, bob, charlie]

collection.stream() creates stream. Chain operations, end with terminal.

stream Sequence of elements supporting functional operations. Lazy until terminal.

Filter and map

Select and transform elements.

example
FilterMap.java
Replay: real traced execution (multi-file project)
import java.util.*;
import java.util.stream.*;

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

        int divisor = 2;
        int scale = 2;
        List<Integer> evensDoubled = nums.stream()
                .filter(n -> n % divisor == 0)
                .map(n -> n * scale)
                .collect(Collectors.toList());

        System.out.println("nums: " + nums);
        System.out.println("evensDoubled: " + evensDoubled);

        List<String> words = Arrays.asList("java", "stream", "api");
        List<Integer> lengths = words.stream()
                .map(String::length)
                .collect(Collectors.toList());

        System.out.println("words: " + words);
        System.out.println("lengths: " + lengths);

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

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

        int divisor = 3;
        int scale = 2;
        List<Integer> evensDoubled = nums.stream()
                .filter(n -> n % divisor == 0)
                .map(n -> n * scale)
                .collect(Collectors.toList());

        System.out.println("nums: " + nums);
        System.out.println("evensDoubled: " + evensDoubled);

        List<String> words = Arrays.asList("java", "stream", "api");
        List<Integer> lengths = words.stream()
                .map(String::length)
                .collect(Collectors.toList());

        System.out.println("words: " + words);
        System.out.println("lengths: " + lengths);

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

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

        int divisor = 2;
        int scale = 10;
        List<Integer> evensDoubled = nums.stream()
                .filter(n -> n % divisor == 0)
                .map(n -> n * scale)
                .collect(Collectors.toList());

        System.out.println("nums: " + nums);
        System.out.println("evensDoubled: " + evensDoubled);

        List<String> words = Arrays.asList("java", "stream", "api");
        List<Integer> lengths = words.stream()
                .map(String::length)
                .collect(Collectors.toList());

        System.out.println("words: " + words);
        System.out.println("lengths: " + lengths);

    }
}
  1. nums ← [1, 2, 3, 4, 5, 6], divisor ← 2, scale ← 2, evensDoubled ← [4, 8, 12]

    4public class FilterMap {5    public static void main(String[] args) {6        List<Integer> nums→ [1, 2, 3, 4, 5, 6] = Arrays.asList(1, 2, 3, 4, 5, 6);78        int divisor→ 2 = 2; //@divisor=2, 39        int scale→ 2 = 2; //@scale=2, 1010        List<Integer> evensDoubled→ [4, 8, 12] = nums.stream()11                .filter(n -> n % divisor == 0)12                .map(n -> n * scale)13                .collect(Collectors.toList());1415        System.out.println("nums: " + nums[1, 2, 3, 4, 5, 6]);16        System.out.println("evensDoubled: " + evensDoubled[4, 8, 12]);1718        List<String> words→ [java, stream, api] = Arrays.asList("java", "stream", "api");19        List<Integer> lengths→ [4, 6, 3] = words.stream()20                .map(String::length)21                .collect(Collectors.toList());2223        System.out.println("words: " + words[java, stream, api]);24        System.out.println("lengths: " + lengths[4, 6, 3]);
    outputnums: [1, 2, 3, 4, 5, 6]
    evensDoubled: [4, 8, 12]
    words: [java, stream, api]
    lengths: [4, 6, 3]
  1. nums ← [1, 2, 3, 4, 5, 6], divisor ← 3, scale ← 2, evensDoubled ← [6, 12]

    4public class FilterMap {5    public static void main(String[] args) {6        List<Integer> nums→ [1, 2, 3, 4, 5, 6] = Arrays.asList(1, 2, 3, 4, 5, 6);78        int divisor→ 3 = 3;9        int scale→ 2 = 2;10        List<Integer> evensDoubled→ [6, 12] = nums.stream()11                .filter(n -> n % divisor == 0)12                .map(n -> n * scale)13                .collect(Collectors.toList());1415        System.out.println("nums: " + nums[1, 2, 3, 4, 5, 6]);16        System.out.println("evensDoubled: " + evensDoubled[6, 12]);1718        List<String> words→ [java, stream, api] = Arrays.asList("java", "stream", "api");19        List<Integer> lengths→ [4, 6, 3] = words.stream()20                .map(String::length)21                .collect(Collectors.toList());2223        System.out.println("words: " + words[java, stream, api]);24        System.out.println("lengths: " + lengths[4, 6, 3]);
    outputnums: [1, 2, 3, 4, 5, 6]
    evensDoubled: [6, 12]
    words: [java, stream, api]
    lengths: [4, 6, 3]
  1. nums ← [1, 2, 3, 4, 5, 6], divisor ← 2, scale ← 10, evensDoubled ← [20, 40, 60]

    4public class FilterMap {5    public static void main(String[] args) {6        List<Integer> nums→ [1, 2, 3, 4, 5, 6] = Arrays.asList(1, 2, 3, 4, 5, 6);78        int divisor→ 2 = 2;9        int scale→ 10 = 10;10        List<Integer> evensDoubled→ [20, 40, 60] = nums.stream()11                .filter(n -> n % divisor == 0)12                .map(n -> n * scale)13                .collect(Collectors.toList());1415        System.out.println("nums: " + nums[1, 2, 3, 4, 5, 6]);16        System.out.println("evensDoubled: " + evensDoubled[20, 40, 60]);1718        List<String> words→ [java, stream, api] = Arrays.asList("java", "stream", "api");19        List<Integer> lengths→ [4, 6, 3] = words.stream()20                .map(String::length)21                .collect(Collectors.toList());2223        System.out.println("words: " + words[java, stream, api]);24        System.out.println("lengths: " + lengths[4, 6, 3]);
    outputnums: [1, 2, 3, 4, 5, 6]
    evensDoubled: [20, 40, 60]
    words: [java, stream, api]
    lengths: [4, 6, 3]

filter(predicate) keeps matching. map(function) transforms each element.

Reduce

Combine elements into single result.

nums
Reduce.java
Replay: real traced execution (multi-file project)
import java.util.*;

public class Reduce {
    public static void main(String[] args) {
        List<Integer> nums = Arrays.asList(1, 2, 3, 4);

        int sum = nums.stream().reduce(0, (a, b) -> a + b);
        System.out.println("sum = " + sum);

        int product = nums.stream().reduce(1, (a, b) -> a * b);
        System.out.println("product = " + product);

        Optional<Integer> max = nums.stream().reduce((a, b) -> a > b ? a : b);
        System.out.println("max = " + max.orElse(-1));

    }
}
import java.util.*;

public class Reduce {
    public static void main(String[] args) {
        List<Integer> nums = Arrays.asList(2, 4, 6);

        int sum = nums.stream().reduce(0, (a, b) -> a + b);
        System.out.println("sum = " + sum);

        int product = nums.stream().reduce(1, (a, b) -> a * b);
        System.out.println("product = " + product);

        Optional<Integer> max = nums.stream().reduce((a, b) -> a > b ? a : b);
        System.out.println("max = " + max.orElse(-1));

    }
}
import java.util.*;

public class Reduce {
    public static void main(String[] args) {
        List<Integer> nums = Arrays.asList(5, 10);

        int sum = nums.stream().reduce(0, (a, b) -> a + b);
        System.out.println("sum = " + sum);

        int product = nums.stream().reduce(1, (a, b) -> a * b);
        System.out.println("product = " + product);

        Optional<Integer> max = nums.stream().reduce((a, b) -> a > b ? a : b);
        System.out.println("max = " + max.orElse(-1));

    }
}
  1. nums ← [1, 2, 3, 4], sum ← 10, product ← 24, max ← Optional[4]

    3public class Reduce {4    public static void main(String[] args) {5        List<Integer> nums→ [1, 2, 3, 4] = Arrays.asList(1, 2, 3, 4); //@nums=Arrays.asList(1, 2, 3, 4), Arrays.asList(2, 4, 6), Arrays.asList(5, 10)67        int sum→ 10 = nums.stream().reduce(0, (a, b) -> a + b);8        System.out.println("sum = " + sum10);910        int product→ 24 = nums.stream().reduce(1, (a, b) -> a * b);11        System.out.println("product = " + product24);1213        Optional<Integer> max→ Optional[4] = nums.stream().reduce((a, b) -> a > b ? a : b);14        System.out.println("max = " + max.orElse(-1));
    outputsum = 10
    product = 24
    max = 4
  1. nums ← [2, 4, 6], sum ← 12, product ← 48, max ← Optional[6]

    3public class Reduce {4    public static void main(String[] args) {5        List<Integer> nums→ [2, 4, 6] = Arrays.asList(2, 4, 6);67        int sum→ 12 = nums.stream().reduce(0, (a, b) -> a + b);8        System.out.println("sum = " + sum12);910        int product→ 48 = nums.stream().reduce(1, (a, b) -> a * b);11        System.out.println("product = " + product48);1213        Optional<Integer> max→ Optional[6] = nums.stream().reduce((a, b) -> a > b ? a : b);14        System.out.println("max = " + max.orElse(-1));
    outputsum = 12
    product = 48
    max = 6
  1. nums ← [5, 10], sum ← 15, product ← 50, max ← Optional[10]

    3public class Reduce {4    public static void main(String[] args) {5        List<Integer> nums→ [5, 10] = Arrays.asList(5, 10);67        int sum→ 15 = nums.stream().reduce(0, (a, b) -> a + b);8        System.out.println("sum = " + sum15);910        int product→ 50 = nums.stream().reduce(1, (a, b) -> a * b);11        System.out.println("product = " + product50);1213        Optional<Integer> max→ Optional[10] = nums.stream().reduce((a, b) -> a > b ? a : b);14        System.out.println("max = " + max.orElse(-1));
    outputsum = 15
    product = 50
    max = 10

reduce(identity, accumulator) - sum, product, concatenation, etc.

reduce Combine stream to single value: `reduce(0, (a,b) -> a+b)` sums integers.

Sort and distinct

Order and deduplicate.

names
SortedDistinct.java
Replay: real traced execution (multi-file project)
import java.util.*;
import java.util.stream.*;

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

        List<String> distinct = names.stream()
                .distinct()
                .collect(Collectors.toList());
        System.out.println("distinct (case-sensitive): " + distinct);

        List<String> normalizedDistinct = names.stream()
                .map(String::toLowerCase)
                .distinct()
                .sorted()
                .collect(Collectors.toList());

        System.out.println("normalizedDistinct: " + normalizedDistinct);

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

public class SortedDistinct {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Java", "java", "Stream", "stream");

        List<String> distinct = names.stream()
                .distinct()
                .collect(Collectors.toList());
        System.out.println("distinct (case-sensitive): " + distinct);

        List<String> normalizedDistinct = names.stream()
                .map(String::toLowerCase)
                .distinct()
                .sorted()
                .collect(Collectors.toList());

        System.out.println("normalizedDistinct: " + normalizedDistinct);

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

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

        List<String> distinct = names.stream()
                .distinct()
                .collect(Collectors.toList());
        System.out.println("distinct (case-sensitive): " + distinct);

        List<String> normalizedDistinct = names.stream()
                .map(String::toLowerCase)
                .distinct()
                .sorted()
                .collect(Collectors.toList());

        System.out.println("normalizedDistinct: " + normalizedDistinct);

    }
}
  1. names ← [Bob, alice, Alice, bob, Charlie], distinct ← [Bob, alice, Alice, bob, Charlie]

    4public class SortedDistinct {5    public static void main(String[] args) {6        List<String> names→ [Bob, alice, Alice, bob, Charlie] = Arrays.asList("Bob", "alice", "Alice", "bob", "Charlie"); //@names=Arrays.asList("Bob", "alice", "Alice", "bob", "Charlie"), Arrays.asList("Java", "java", "Stream", "stream"), Arrays.asList("Ann", "Bob", "Ann")78        List<String> distinct→ [Bob, alice, Alice, bob, Charlie] = names.stream()9                .distinct()10                .collect(Collectors.toList());11        System.out.println("distinct (case-sensitive): " + distinct[Bob, alice, Alice, bob, Charlie]);1213        List<String> normalizedDistinct→ [alice, bob, charlie] = names.stream()14                .map(String::toLowerCase)15                .distinct()16                .sorted()17                .collect(Collectors.toList());1819        System.out.println("normalizedDistinct: " + normalizedDistinct[alice, bob, charlie]);
    outputdistinct (case-sensitive): [Bob, alice, Alice, bob, Charlie]
    normalizedDistinct: [alice, bob, charlie]
  1. names ← [Java, java, Stream, stream], distinct ← [Java, java, Stream, stream]

    4public class SortedDistinct {5    public static void main(String[] args) {6        List<String> names→ [Java, java, Stream, stream] = Arrays.asList("Java", "java", "Stream", "stream");78        List<String> distinct→ [Java, java, Stream, stream] = names.stream()9                .distinct()10                .collect(Collectors.toList());11        System.out.println("distinct (case-sensitive): " + distinct[Java, java, Stream, stream]);1213        List<String> normalizedDistinct→ [java, stream] = names.stream()14                .map(String::toLowerCase)15                .distinct()16                .sorted()17                .collect(Collectors.toList());1819        System.out.println("normalizedDistinct: " + normalizedDistinct[java, stream]);
    outputdistinct (case-sensitive): [Java, java, Stream, stream]
    normalizedDistinct: [java, stream]
  1. names ← [Ann, Bob, Ann], distinct ← [Ann, Bob], normalizedDistinct ← [ann, bob]

    4public class SortedDistinct {5    public static void main(String[] args) {6        List<String> names→ [Ann, Bob, Ann] = Arrays.asList("Ann", "Bob", "Ann");78        List<String> distinct→ [Ann, Bob] = names.stream()9                .distinct()10                .collect(Collectors.toList());11        System.out.println("distinct (case-sensitive): " + distinct[Ann, Bob]);1213        List<String> normalizedDistinct→ [ann, bob] = names.stream()14                .map(String::toLowerCase)15                .distinct()16                .sorted()17                .collect(Collectors.toList());1819        System.out.println("normalizedDistinct: " + normalizedDistinct[ann, bob]);
    outputdistinct (case-sensitive): [Ann, Bob]
    normalizedDistinct: [ann, bob]

sorted() natural order. sorted(comparator) custom. distinct() removes duplicates.

Collectors

Gather stream results into collections.

words
Collectors.java
Replay: real traced execution (multi-file project)
import java.util.*;
import java.util.stream.*;

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

        String joined = words.stream()
                .map(String::toUpperCase)
                .collect(java.util.stream.Collectors.joining(", "));
        System.out.println("joined: " + joined);

        Map<Character, List<String>> byFirstLetter = words.stream()
                .collect(java.util.stream.Collectors.groupingBy(
                        w -> w.charAt(0),
                        LinkedHashMap::new,
                        java.util.stream.Collectors.toList()));
        System.out.println("byFirstLetter: " + byFirstLetter);

        Map<Character, Long> counts = words.stream()
                .collect(java.util.stream.Collectors.groupingBy(
                        w -> w.charAt(0),
                        LinkedHashMap::new,
                        java.util.stream.Collectors.counting()));
        System.out.println("counts: " + counts);

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

public class Collectors {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("red", "rose", "blue", "black");

        String joined = words.stream()
                .map(String::toUpperCase)
                .collect(java.util.stream.Collectors.joining(", "));
        System.out.println("joined: " + joined);

        Map<Character, List<String>> byFirstLetter = words.stream()
                .collect(java.util.stream.Collectors.groupingBy(
                        w -> w.charAt(0),
                        LinkedHashMap::new,
                        java.util.stream.Collectors.toList()));
        System.out.println("byFirstLetter: " + byFirstLetter);

        Map<Character, Long> counts = words.stream()
                .collect(java.util.stream.Collectors.groupingBy(
                        w -> w.charAt(0),
                        LinkedHashMap::new,
                        java.util.stream.Collectors.counting()));
        System.out.println("counts: " + counts);

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

public class Collectors {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("cat", "cow", "dog");

        String joined = words.stream()
                .map(String::toUpperCase)
                .collect(java.util.stream.Collectors.joining(", "));
        System.out.println("joined: " + joined);

        Map<Character, List<String>> byFirstLetter = words.stream()
                .collect(java.util.stream.Collectors.groupingBy(
                        w -> w.charAt(0),
                        LinkedHashMap::new,
                        java.util.stream.Collectors.toList()));
        System.out.println("byFirstLetter: " + byFirstLetter);

        Map<Character, Long> counts = words.stream()
                .collect(java.util.stream.Collectors.groupingBy(
                        w -> w.charAt(0),
                        LinkedHashMap::new,
                        java.util.stream.Collectors.counting()));
        System.out.println("counts: " + counts);

    }
}
  1. words ← [apple, apricot, banana, blueberry, cherry], joined ← APPLE, APRICOT, BANANA, BLUEBERRY, CHERRY

    4public class Collectors {5    public static void main(String[] args) {6        List<String> words→ [apple, apricot, banana, blueberry, cherry] = Arrays.asList("apple", "apricot", "banana", "blueberry", "cherry"); //@words=Arrays.asList("apple", "apricot", "banana", "blueberry", "cherry"), Arrays.asList("red", "rose", "blue", "black"), Arrays.asList("cat", "cow", "dog")78        String joined→ APPLE, APRICOT, BANANA, BLUEBERRY, CHERRY = words.stream()9                .map(String::toUpperCase)10                .collect(java.util.stream.Collectors.joining(", "));11        System.out.println("joined: " + joinedAPPLE, APRICOT, BANANA, BLUEBERRY, CHERRY);1213        Map<Character, List<String>> byFirstLetter→ {a=[apple, apricot], b=[banana, blueberry], c=[cherry]} = words.stream()14                .collect(java.util.stream.Collectors.groupingBy(15                        w -> w.charAt(0),16                        LinkedHashMap::new,17                        java.util.stream.Collectors.toList()));18        System.out.println("byFirstLetter: " + byFirstLetter{a=[apple, apricot], b=[banana, blueberry], c=[cherry]});1920        Map<Character, Long> counts→ {a=2, b=2, c=1} = words.stream()21                .collect(java.util.stream.Collectors.groupingBy(22                        w -> w.charAt(0),23                        LinkedHashMap::new,24                        java.util.stream.Collectors.counting()));25        System.out.println("counts: " + counts{a=2, b=2, c=1});
    outputjoined: APPLE, APRICOT, BANANA, BLUEBERRY, CHERRY
    byFirstLetter: {a=[apple, apricot], b=[banana, blueberry], c=[cherry]}
    counts: {a=2, b=2, c=1}
  1. words ← [red, rose, blue, black], joined ← RED, ROSE, BLUE, BLACK

    4public class Collectors {5    public static void main(String[] args) {6        List<String> words→ [red, rose, blue, black] = Arrays.asList("red", "rose", "blue", "black");78        String joined→ RED, ROSE, BLUE, BLACK = words.stream()9                .map(String::toUpperCase)10                .collect(java.util.stream.Collectors.joining(", "));11        System.out.println("joined: " + joinedRED, ROSE, BLUE, BLACK);1213        Map<Character, List<String>> byFirstLetter→ {r=[red, rose], b=[blue, black]} = words.stream()14                .collect(java.util.stream.Collectors.groupingBy(15                        w -> w.charAt(0),16                        LinkedHashMap::new,17                        java.util.stream.Collectors.toList()));18        System.out.println("byFirstLetter: " + byFirstLetter{r=[red, rose], b=[blue, black]});1920        Map<Character, Long> counts→ {r=2, b=2} = words.stream()21                .collect(java.util.stream.Collectors.groupingBy(22                        w -> w.charAt(0),23                        LinkedHashMap::new,24                        java.util.stream.Collectors.counting()));25        System.out.println("counts: " + counts{r=2, b=2});
    outputjoined: RED, ROSE, BLUE, BLACK
    byFirstLetter: {r=[red, rose], b=[blue, black]}
    counts: {r=2, b=2}
  1. words ← [cat, cow, dog], joined ← CAT, COW, DOG, byFirstLetter ← {c=[cat, cow], d=[dog]}

    4public class Collectors {5    public static void main(String[] args) {6        List<String> words→ [cat, cow, dog] = Arrays.asList("cat", "cow", "dog");78        String joined→ CAT, COW, DOG = words.stream()9                .map(String::toUpperCase)10                .collect(java.util.stream.Collectors.joining(", "));11        System.out.println("joined: " + joinedCAT, COW, DOG);1213        Map<Character, List<String>> byFirstLetter→ {c=[cat, cow], d=[dog]} = words.stream()14                .collect(java.util.stream.Collectors.groupingBy(15                        w -> w.charAt(0),16                        LinkedHashMap::new,17                        java.util.stream.Collectors.toList()));18        System.out.println("byFirstLetter: " + byFirstLetter{c=[cat, cow], d=[dog]});1920        Map<Character, Long> counts→ {c=2, d=1} = words.stream()21                .collect(java.util.stream.Collectors.groupingBy(22                        w -> w.charAt(0),23                        LinkedHashMap::new,24                        java.util.stream.Collectors.counting()));25        System.out.println("counts: " + counts{c=2, d=1});
    outputjoined: CAT, COW, DOG
    byFirstLetter: {c=[cat, cow], d=[dog]}
    counts: {c=2, d=1}

collect(Collectors.toList()), toSet(), toMap(), groupingBy().

Collectors Terminal operation to gather results: `toList()`, `toMap()`, `groupingBy()`.

Exercise: Practical.java

Process sales data with stream pipeline