Processing collections often requires selecting specific items and transforming them. Stream's filter and map operations let you declaratively express "keep items matching X, then transform each to Y" without manual loops or intermediate collections.

Filtering

The filter operation takes a Predicate<T> and returns a stream containing only elements where the predicate returns true.

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

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

        List<Integer> evens = nums.stream()
                .filter(n -> n % 2 == 0)
                .collect(Collectors.toList());
        System.out.println("evens: " + evens);

        List<Integer> large = nums.stream()
                .filter(n -> n > minValue)
                .collect(Collectors.toList());
        System.out.println("large: " + large);

        List<Integer> evenAndLarge = nums.stream()
                .filter(n -> n % 2 == 0)
                .filter(n -> n > 4)
                .collect(Collectors.toList());
        System.out.println("evenAndLarge: " + evenAndLarge);

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

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

        List<Integer> evens = nums.stream()
                .filter(n -> n % 2 == 0)
                .collect(Collectors.toList());
        System.out.println("evens: " + evens);

        List<Integer> large = nums.stream()
                .filter(n -> n > minValue)
                .collect(Collectors.toList());
        System.out.println("large: " + large);

        List<Integer> evenAndLarge = nums.stream()
                .filter(n -> n % 2 == 0)
                .filter(n -> n > 4)
                .collect(Collectors.toList());
        System.out.println("evenAndLarge: " + evenAndLarge);

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

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

        List<Integer> evens = nums.stream()
                .filter(n -> n % 2 == 0)
                .collect(Collectors.toList());
        System.out.println("evens: " + evens);

        List<Integer> large = nums.stream()
                .filter(n -> n > minValue)
                .collect(Collectors.toList());
        System.out.println("large: " + large);

        List<Integer> evenAndLarge = nums.stream()
                .filter(n -> n % 2 == 0)
                .filter(n -> n > 4)
                .collect(Collectors.toList());
        System.out.println("evenAndLarge: " + evenAndLarge);

    }
}
filter An intermediate operation that keeps only elements matching a predicate, discarding the rest.

Mapping

The map operation takes a Function<T, R> and produces a new stream where each element has been transformed.

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

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

        List<String> upper = words.stream()
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        System.out.println("upper: " + upper);

        List<Integer> lengths = words.stream()
                .map(String::length)
                .collect(Collectors.toList());
        System.out.println("lengths: " + lengths);

        List<String> transformed = words.stream()
                .map(s -> s.toUpperCase())
                .map(s -> s + suffix)
                .collect(Collectors.toList());
        System.out.println("transformed: " + transformed);

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

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

        List<String> upper = words.stream()
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        System.out.println("upper: " + upper);

        List<Integer> lengths = words.stream()
                .map(String::length)
                .collect(Collectors.toList());
        System.out.println("lengths: " + lengths);

        List<String> transformed = words.stream()
                .map(s -> s.toUpperCase())
                .map(s -> s + suffix)
                .collect(Collectors.toList());
        System.out.println("transformed: " + transformed);

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

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

        List<String> upper = words.stream()
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        System.out.println("upper: " + upper);

        List<Integer> lengths = words.stream()
                .map(String::length)
                .collect(Collectors.toList());
        System.out.println("lengths: " + lengths);

        List<String> transformed = words.stream()
                .map(s -> s.toUpperCase())
                .map(s -> s + suffix)
                .collect(Collectors.toList());
        System.out.println("transformed: " + transformed);

    }
}
map An intermediate operation that transforms each element by applying a function.

Chaining Filter and Map

Filter and map compose naturally into pipelines. You can chain multiple operations, and they execute lazily only when a terminal operation is called.

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

public class Chain {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "a", "banana", "b", "cherry", "");
        int minLength = ;

        List<String> result = words.stream()
                .filter(s -> s.length() > minLength)
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        System.out.println("result: " + result);

        List<Integer> nums = Arrays.asList(-5, -2, 0, 3, 7);
        List<Integer> processed = nums.stream()
                .map(n -> n * n)
                .filter(n -> n > 10)
                .collect(Collectors.toList());
        System.out.println("processed: " + processed);

        List<String> names = Arrays.asList("  Alice ", " bob", "CHARLIE  ");
        List<String> normalized = names.stream()
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .map(String::toLowerCase)
                .sorted()
                .collect(Collectors.toList());
        System.out.println("normalized: " + normalized);

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

public class Chain {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "a", "banana", "b", "cherry", "");
        int minLength = ;

        List<String> result = words.stream()
                .filter(s -> s.length() > minLength)
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        System.out.println("result: " + result);

        List<Integer> nums = Arrays.asList(-5, -2, 0, 3, 7);
        List<Integer> processed = nums.stream()
                .map(n -> n * n)
                .filter(n -> n > 10)
                .collect(Collectors.toList());
        System.out.println("processed: " + processed);

        List<String> names = Arrays.asList("  Alice ", " bob", "CHARLIE  ");
        List<String> normalized = names.stream()
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .map(String::toLowerCase)
                .sorted()
                .collect(Collectors.toList());
        System.out.println("normalized: " + normalized);

    }
}

FlatMap

When a mapping function produces a stream for each element (e.g., splitting strings into words), use flatMap to flatten the nested streams into a single stream.

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

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

        List<Integer> flat = nested.stream()
                .flatMap(list -> list.stream())
                .collect(Collectors.toList());
        System.out.println("flat: " + flat);

        List<String> sentences = ;
        List<String> allWords = sentences.stream()
                .flatMap(s -> Arrays.stream(s.split(" ")))
                .collect(Collectors.toList());
        System.out.println("allWords: " + allWords);

        List<Integer> evensFlat = nested.stream()
                .flatMap(list -> list.stream())
                .filter(n -> n % 2 == 0)
                .collect(Collectors.toList());
        System.out.println("evensFlat: " + evensFlat);

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

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

        List<Integer> flat = nested.stream()
                .flatMap(list -> list.stream())
                .collect(Collectors.toList());
        System.out.println("flat: " + flat);

        List<String> sentences = ;
        List<String> allWords = sentences.stream()
                .flatMap(s -> Arrays.stream(s.split(" ")))
                .collect(Collectors.toList());
        System.out.println("allWords: " + allWords);

        List<Integer> evensFlat = nested.stream()
                .flatMap(list -> list.stream())
                .filter(n -> n % 2 == 0)
                .collect(Collectors.toList());
        System.out.println("evensFlat: " + evensFlat);

    }
}
flatMap Transforms each element into a stream, then flattens all streams into one.

Primitive Streams

Use specialized streams for primitives to avoid boxing overhead:

  • mapToInt, mapToDouble, mapToLong convert to primitive streams
  • IntStream, DoubleStream, LongStream provide specialized operations
PrimitiveStreams.java
import java.util.*;
import java.util.stream.*;

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

        int totalLength = words.stream()
                .mapToInt(String::length)
                .sum();
        System.out.println("totalLength: " + totalLength);

        List<Integer> squares = IntStream.range(1, rangeEnd)
                .map(n -> n * n)
                .boxed()
                .collect(Collectors.toList());
        System.out.println("squares: " + squares);

        List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
        double avg = nums.stream()
                .mapToDouble(Integer::doubleValue)
                .average()
                .orElse(0.0);
        System.out.println("avg: " + avg);

        IntStream.of(3, 1, 4, 1, 5)
                .sorted()
                .distinct()
                .forEach(n -> System.out.print(n + " "));
        System.out.println();

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

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

        int totalLength = words.stream()
                .mapToInt(String::length)
                .sum();
        System.out.println("totalLength: " + totalLength);

        List<Integer> squares = IntStream.range(1, rangeEnd)
                .map(n -> n * n)
                .boxed()
                .collect(Collectors.toList());
        System.out.println("squares: " + squares);

        List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
        double avg = nums.stream()
                .mapToDouble(Integer::doubleValue)
                .average()
                .orElse(0.0);
        System.out.println("avg: " + avg);

        IntStream.of(3, 1, 4, 1, 5)
                .sorted()
                .distinct()
                .forEach(n -> System.out.print(n + " "));
        System.out.println();

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

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

        int totalLength = words.stream()
                .mapToInt(String::length)
                .sum();
        System.out.println("totalLength: " + totalLength);

        List<Integer> squares = IntStream.range(1, rangeEnd)
                .map(n -> n * n)
                .boxed()
                .collect(Collectors.toList());
        System.out.println("squares: " + squares);

        List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
        double avg = nums.stream()
                .mapToDouble(Integer::doubleValue)
                .average()
                .orElse(0.0);
        System.out.println("avg: " + avg);

        IntStream.of(3, 1, 4, 1, 5)
                .sorted()
                .distinct()
                .forEach(n -> System.out.print(n + " "));
        System.out.println();

    }
}

Exercise: Practical.java

Filter a list of products by price range, then map to product names