Comprehensive Guide to Java Stream API and Optional Usage

This article provides an in‑depth tutorial on Java 8 Stream operations—including creation, intermediate and terminal operations, parallel streams, reduction, collection, and conversion—followed by a detailed explanation of the Optional class with examples of creation, presence checks, default values, filtering, mapping, and practical usage patterns.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Comprehensive Guide to Java Stream API and Optional Usage

Preface

I have long admired Java Stream and finally have the chance to explore its features and usage thoroughly; understanding its source code deeply will require further study, and while learning Stream I also discovered the powerful Optional class.

Stream

Stream operations can be divided into two types:

1) Intermediate operations, which may appear multiple times, each returning a new stream and allowing method chaining.

2) Terminal operations, which can appear only once; after execution the stream is consumed and cannot be used further, so it must be placed at the end.

Intermediate operations are lazy; they are not executed until a terminal operation triggers actual traversal, enabling multiple actions in a single pass and greatly improving performance.

0x1. Creating Streams

For arrays you can use Arrays.stream() or Stream.of(); for collections you can call the stream() method added to the Collection interface.

public static void main(String[] args) {
    String[] arr = new String[]{"a", "b", "c"};
    Stream<String> stream = Arrays.stream(arr);
    stream = Stream.of("a", "b", "c");
    List<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    stream = list.stream();
}

Inspecting Stream source shows that of() internally calls Arrays.stream().

public static <T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}

Collections also provide parallelStream(), which creates a parallel stream using the default ForkJoinPool.commonPool().

List<Long> aList = new ArrayList<>();
Stream<Long> parallelStream = aList.parallelStream();

0x2. Operating on Streams

0x2.1. Filtering

Use filter() to select elements that satisfy a predicate.

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("周杰伦");
    list.add("王力宏");
    list.add("陶喆");
    list.add("林俊杰");
    Stream<String> stream = list.stream().filter(element -> element.contains("王"));
    stream.forEach(System.out::println);
}

Output:

王力宏

0x2.2. Mapping

Use map() to transform each element, e.g., converting strings to their lengths.

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("周杰伦");
    list.add("王力宏");
    list.add("陶喆");
    list.add("林俊杰");
    Stream<Integer> stream = list.stream().map(String::length);
    stream.forEach(System.out::println);
}

Output:

3
3
2
3

0x2.3. Matching

Stream provides anyMatch(), allMatch(), and noneMatch() for element matching.

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("周杰伦");
    list.add("王力宏");
    list.add("陶喆");
    list.add("林俊杰");
    boolean anyMatchFlag = list.stream().anyMatch(e -> e.contains("王"));
    boolean allMatchFlag = list.stream().allMatch(e -> e.length() > 1);
    boolean noneMatchFlag = list.stream().noneMatch(e -> e.endsWith("沉"));
    System.out.println(anyMatchFlag);
    System.out.println(allMatchFlag);
    System.out.println(noneMatchFlag);
}

Output:

true
true
true

0x2.4. Reducing

The reduce() method combines stream elements. Two forms exist:

Optional<T> reduce(BinaryOperator<T> accumulator)

No identity value; returns an Optional.

T reduce(T identity, BinaryOperator<T> accumulator)

With identity; returns a value of the same type as the identity.

public static void main(String[] args) {
    Integer[] ints = {0, 1, 2, 3};
    List<Integer> list = Arrays.asList(ints);
    Optional<Integer> optional = list.stream().reduce((a, b) -> a + b);
    Optional<Integer> optional1 = list.stream().reduce(Integer::sum);
    System.out.println(optional.orElse(0));
    System.out.println(optional1.orElse(0));
    int reduce = list.stream().reduce(6, (a, b) -> a + b);
    System.out.println(reduce);
    int reduce1 = list.stream().reduce(6, Integer::sum);
    System.out.println(reduce1);
}

Output:

6
6
12
12

0x3. Converting Streams

Use collect() to transform a stream back into a collection or other data structures.

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("周杰伦");
    list.add("王力宏");
    list.add("陶喆");
    list.add("林俊杰");
    String[] strArray = list.stream().toArray(String[]::new);
    System.out.println(Arrays.toString(strArray));
    List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList());
    List<String> list2 = list.stream().collect(Collectors.toCollection(ArrayList::new));
    System.out.println(list1);
    System.out.println(list2);
    String str = list.stream().collect(Collectors.joining(", ")).toString();
    System.out.println(str);
}

Output:

[周杰伦, 王力宏, 陶喆, 林俊杰]
[3, 3, 2, 3]
[周杰伦, 王力宏, 陶喆, 林俊杰]
周杰伦, 王力宏, 陶喆, 林俊杰

0x4. Full Example

import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.Map;

public class Java8Tester {
    public static void main(String args[]) {
        // Count empty strings
        List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
        System.out.println("列表: " + strings);
        long count = strings.stream().filter(string -> string.isEmpty()).count();
        System.out.println("空字符串数量为: " + count);
        count = strings.stream().filter(string -> string.length() == 3).count();
        System.out.println("字符串长度为 3 的数量为: " + count);
        List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
        System.out.println("筛选后的列表: " + filtered);
        String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
        System.out.println("合并字符串: " + mergedString);
        // Additional numeric example omitted for brevity
        long parallelCount = strings.parallelStream().filter(string -> string.isEmpty()).count();
        System.out.println("空字符串的数量为: " + parallelCount);
    }
}

Sample output demonstrates counting, filtering, joining, and parallel processing of collections.

Optional

The Optional class provides a container for possibly absent values, avoiding null references.

0x1. Creating Optional Objects

Use Optional.empty() for an empty optional, Optional.of() for a non‑null value, and Optional.ofNullable() for a value that may be null.

Optional<String> empty = Optional.empty();
System.out.println(empty); // Optional.empty
Optional<String> opt = Optional.of("id10t.");
System.out.println(opt); // Optional[id10t.]
String name = "id10t.";
Optional<String> optOrNull = Optional.ofNullable(name);
System.out.println(optOrNull); // Optional[id10t.]
optOrNull = Optional.ofNullable(null);
System.out.println(optOrNull); // Optional.empty

0x2. Checking Presence

Use isPresent() (or Java 11+ isEmpty()) to test whether a value exists.

Optional<String> opt = Optional.of("id10t.");
System.out.println(opt.isPresent()); // true
Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(optOrNull.isPresent()); // false

0x3. Non‑Null Expressions

ifPresent()

executes a consumer when the value is present; Java 9+ adds ifPresentOrElse() for both present and absent cases.

Optional<String> opt = Optional.of("id10t.");
opt.ifPresent(str -> System.out.println(str.length()));
opt.ifPresentOrElse(str -> System.out.println(str.length()), () -> System.out.println("null"));

0x4. Providing Default Values

orElse()

returns the contained value or a default; orElseGet() lazily supplies the default via a supplier, avoiding unnecessary computation.

String nullName = null;
String name = Optional.ofNullable(nullName).orElse("id10t.");
System.out.println(name); // id10t.
String name2 = Optional.ofNullable(nullName).orElseGet(() -> "id10t.");
System.out.println(name2); // id10t.

0x5. Filtering Values

filter()

applies a Predicate to an optional, returning either the original optional or Optional.empty().

String password = "123456";
Optional<String> opt = Optional.ofNullable(password);
System.out.println(opt.filter(pwd -> pwd.length() > 6).isPresent()); // false
Predicate<String> len6 = pwd -> pwd.length() > 6;
Predicate<String> len10 = pwd -> pwd.length() < 10;
password = "1234567";
opt = Optional.ofNullable(password);
boolean result = opt.filter(len6.and(len10)).isPresent();
System.out.println(result); // true

0x6. Transforming Values

map()

converts the contained value to another type, producing a new Optional.

String name = "id10t.";
Optional<String> nameOptional = Optional.of(name);
Optional<Integer> intOpt = nameOptional.map(String::length);
System.out.println(intOpt.orElse(0)); // 7
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.

functional programmingCollectionsoptionalStream APIJava 8
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

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.