Master Java 8 Streams: From Basics to Advanced Operations

This article introduces Java 8's Stream API, explains why functional streams improve code readability and performance, and provides detailed examples of common operations such as filter, map, flatMap, reduce, collect, Optional handling, parallel processing, and debugging techniques for efficient data processing.

Programmer DD
Programmer DD
Programmer DD
Master Java 8 Streams: From Basics to Advanced Operations

Stream Overview

Java 8 introduced a new Stream API. Unlike I/O streams, it behaves like an Iterable collection with distinct characteristics.

Streams enhance collection functionality, focusing on convenient and efficient aggregation or bulk data operations.

By specifying element operations—e.g., filtering strings longer than 10 characters or extracting first letters—Stream implicitly traverses and transforms data.

Why Use Streams

Functional programming makes code express business intent rather than implementation details, resulting in more readable, maintainable, reliable, and less error‑prone code.

Sample Data Source

public class Data {
    private static List<PersonModel> list = null;
    static {
        PersonModel wu = new PersonModel("wu qi", 18, "男");
        PersonModel zhang = new PersonModel("zhang san", 19, "男");
        PersonModel wang = new PersonModel("wang si", 20, "女");
        PersonModel zhao = new PersonModel("zhao wu", 20, "男");
        PersonModel chen = new PersonModel("chen liu", 21, "男");
        list = Arrays.asList(wu, zhang, wang, zhao, chen);
    }
    public static List<PersonModel> getData() {
        return list;
    }
}

Filter

Used to traverse data and test each element.

filter accepts a lambda expression as its predicate.

/**
 * Filter all males
 */
public static void fiterSex() {
    List<PersonModel> data = Data.getData();
    // old way
    List<PersonModel> temp = new ArrayList<>();
    for (PersonModel person : data) {
        if ("男".equals(person.getSex())) {
            temp.add(person);
        }
    }
    System.out.println(temp);
    // new way
    List<PersonModel> collect = data.stream()
        .filter(person -> "男".equals(person.getSex()))
        .collect(toList());
    System.out.println(collect);
}

/**
 * Filter all males and age < 20
 */
public static void fiterSexAndAge() {
    List<PersonModel> data = Data.getData();
    // old way
    List<PersonModel> temp = new ArrayList<>();
    for (PersonModel person : data) {
        if ("男".equals(person.getSex()) && person.getAge() < 20) {
            temp.add(person);
        }
    }
    // new ways
    List<PersonModel> collect = data.stream()
        .filter(person -> {
            if ("男".equals(person.getSex()) && person.getAge() < 20) {
                return true;
            }
            return false;
        })
        .collect(toList());
    List<PersonModel> collect1 = data.stream()
        .filter(person -> "男".equals(person.getSex()) && person.getAge() < 20)
        .collect(toList());
}

Map

Map creates a one‑to‑one transformation.

Commonly used and simple.

/**
 * Extract all user names
 */
public static void getUserNameList(){
    List<PersonModel> data = Data.getData();
    // old way
    List<String> list = new ArrayList<>();
    for (PersonModel persion : data) {
        list.add(persion.getName());
    }
    System.out.println(list);
    // new ways
    List<String> collect = data.stream().map(person -> person.getName()).collect(toList());
    System.out.println(collect);
    List<String> collect1 = data.stream().map(PersonModel::getName).collect(toList());
    System.out.println(collect1);
    List<String> collect2 = data.stream().map(person -> {
        System.out.println(person.getName());
        return person.getName();
    }).collect(toList());
}

FlatMap

Similar to map but handles deeper, one‑to‑many transformations.

Map returns a one‑to‑one mapping; flatMap handles one‑to‑many.

Method signatures differ:

Stream map(Function mapper)

Stream flatMap(Function<? super T, ? extends Stream<? extends R> mapper)

public static void flatMapString() {
    List<PersonModel> data = Data.getData();
    // different return type
    List<String> collect = data.stream()
        .flatMap(person -> Arrays.stream(person.getName().split(" ")))
        .collect(toList());
    List<Stream<String>> collect1 = data.stream()
        .map(person -> Arrays.stream(person.getName().split(" ")))
        .collect(toList());
    // using map then flatMap
    List<String> collect2 = data.stream()
        .map(person -> person.getName().split(" "))
        .flatMap(Arrays::stream)
        .collect(toList());
    // another way
    List<String> collect3 = data.stream()
        .map(person -> person.getName().split(" "))
        .flatMap(str -> Arrays.asList(str).stream())
        .collect(toList());
}

Reduce

Similar to recursion; used for numeric or string aggregation.

public static void reduceTest(){
    // sum with initial value 10
    Integer reduce = Stream.of(1,2,3,4)
        .reduce(10, (count, item) -> {
            System.out.println("count:"+count);
            System.out.println("item:"+item);
            return count + item;
        });
    System.out.println(reduce);
    Integer reduce1 = Stream.of(1,2,3,4)
        .reduce(0, (x, y) -> x + y);
    System.out.println(reduce1);
    String reduce2 = Stream.of("1","2","3")
        .reduce("0", (x, y) -> (x + "," + y));
    System.out.println(reduce2);
}

Collect

Collect generates common data structures such as List, Set, Map, or custom collections from a stream.

/**
 * toList
 */
public static void toListTest(){
    List<PersonModel> data = Data.getData();
    List<String> collect = data.stream()
        .map(PersonModel::getName)
        .collect(Collectors.toList());
}
/**
 * toSet
 */
public static void toSetTest(){
    List<PersonModel> data = Data.getData();
    Set<String> collect = data.stream()
        .map(PersonModel::getName)
        .collect(Collectors.toSet());
}
/**
 * toMap
 */
public static void toMapTest(){
    List<PersonModel> data = Data.getData();
    Map<String, Integer> collect = data.stream()
        .collect(Collectors.toMap(PersonModel::getName, PersonModel::getAge));
    // custom value transformation
    data.stream()
        .collect(Collectors.toMap(per -> per.getName(), value -> {
            return value + "1";
        }));
}
/**
 * Specify collection type
 */
public static void toTreeSetTest(){
    List<PersonModel> data = Data.getData();
    TreeSet<PersonModel> collect = data.stream()
        .collect(Collectors.toCollection(TreeSet::new));
    System.out.println(collect);
}
/**
 * Grouping
 */
public static void toGroupTest(){
    List<PersonModel> data = Data.getData();
    Map<Boolean, List<PersonModel>> collect = data.stream()
        .collect(Collectors.groupingBy(per -> "男".equals(per.getSex())));
    System.out.println(collect);
}
/**
 * Joining
 */
public static void toJoiningTest(){
    List<PersonModel> data = Data.getData();
    String collect = data.stream()
        .map(personModel -> personModel.getName())
        .collect(Collectors.joining(",", "{", "}"));
    System.out.println(collect);
}
/**
 * Custom reduction
 */
public static void reduce(){
    List<String> collect = Stream.of("1","2","3").collect(
        Collectors.reducing(new ArrayList<>(), x -> Arrays.asList(x), (y, z) -> {
            y.addAll(z);
            return y;
        }));
    System.out.println(collect);
}

Optional

Optional is a new container type designed to replace null values, reducing null‑related errors.

It provides methods such as of, ofNullable, isPresent, ifPresent, orElse, orElseGet, and orElseThrow.

public static void main(String[] args) {
    PersonModel personModel = new PersonModel();
    // old style null check
    Optional<Object> o = Optional.of(personModel);
    System.out.println(o.isPresent()?o.get():"-");
    // nullable name
    Optional<String> name = Optional.ofNullable(personModel.getName());
    System.out.println(name.isPresent()?name.get():"-");
    // ifPresent example
    Optional.ofNullable("test").ifPresent(na -> {
        System.out.println(na+"ifPresent");
    });
    // orElse examples
    System.out.println(Optional.ofNullable(null).orElse("-"));
    System.out.println(Optional.ofNullable("1").orElse("-"));
    // orElseGet examples
    System.out.println(Optional.ofNullable(null).orElseGet(() -> {
        return "hahah";
    }));
    System.out.println(Optional.ofNullable("1").orElseGet(() -> {
        return "hahah";
    }));
    // orElseThrow example
    System.out.println(Optional.ofNullable("1").orElseThrow(() -> {
        throw new RuntimeException("ss");
    }));
    // multi‑level checks with Optional
    EarthModel earthModel1 = new EarthModel();
    Optional.ofNullable(earthModel1)
        .map(EarthModel::getTea)
        .map(TeaModel::getType)
        .isPresent();
    // processing list inside Optional
    Optional.ofNullable(new EarthModel())
        .map(EarthModel::getPersonModels)
        .map(pers -> pers.stream()
            .map(PersonModel::getName)
            .collect(toList()))
        .ifPresent(per -> System.out.println(per));
    List<PersonModel> models = Data.getData();
    Optional.ofNullable(models)
        .map(per -> per.stream()
            .map(PersonModel::getName)
            .collect(toList()))
        .ifPresent(per -> System.out.println(per));
}

Concurrency

Replace stream with parallelStream or parallel to enable parallel processing.

Parallel performance depends on data size, source structure, boxing, CPU core count, and per‑element processing time.

private static int size = 10000000;
public static void main(String[] args) {
    System.out.println("-----------List-----------");
    testList();
    System.out.println("-----------Set-----------");
    testSet();
}
/**
 * Test list processing
 */
public static void testList(){
    List<Integer> list = new ArrayList<>(size);
    for (Integer i = 0; i < size; i++) {
        list.add(new Integer(i));
    }
    // old way
    long start = System.currentTimeMillis();
    for (Integer i : list) {
        // no operation
    }
    System.out.println(System.currentTimeMillis() - start);
    // sequential stream
    long start1 = System.currentTimeMillis();
    list.stream().collect(Collectors.toList());
    System.out.println(System.currentTimeMillis() - start1);
    // parallel stream
    long start2 = System.currentTimeMillis();
    list.parallelStream().collect(Collectors.toList());
    System.out.println(System.currentTimeMillis() - start2);
}
/**
 * Test set processing
 */
public static void testSet(){
    List<Integer> list = new ArrayList<>(size);
    for (Integer i = 0; i < size; i++) {
        list.add(new Integer(i));
    }
    // old way
    long start = System.currentTimeMillis();
    for (Integer i : list) {
        // no operation
    }
    System.out.println(System.currentTimeMillis() - start);
    // sequential stream
    long start1 = System.currentTimeMillis();
    list.stream().collect(Collectors.toSet());
    System.out.println(System.currentTimeMillis() - start1);
    // parallel stream
    long start2 = System.currentTimeMillis();
    list.parallelStream().collect(Collectors.toSet());
    System.out.println(System.currentTimeMillis() - start2);
}

Debugging

Stream operations are chained (e.g., list.map.filter.map.collect). Lazy evaluation returns a Stream; eager evaluation returns a concrete result.

peek allows inspection of each element while preserving the stream.

private static void peekTest(){
    List<PersonModel> data = Data.getData();
    // peek prints each name during the stream
    data.stream().map(per -> per.getName())
        .peek(p -> {
            System.out.println(p);
        })
        .collect(toList());
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javadata-processingLambdafunctional programmingStream APIJava 8
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.