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.
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
stream = Arrays.stream(arr);
stream = Stream.of("a", "b", "c");
List
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
Stream
of(T... values) {
return Arrays.stream(values);
}Collections also provide parallelStream() , which creates a parallel stream using the default ForkJoinPool.commonPool() .
List
aList = new ArrayList<>();
Stream
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
list = new ArrayList<>();
list.add("周杰伦");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
Stream
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
list = new ArrayList<>();
list.add("周杰伦");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
Stream
stream = list.stream().map(String::length);
stream.forEach(System.out::println);
}Output:
3
3
2
30x2.3. Matching
Stream provides anyMatch() , allMatch() , and noneMatch() for element matching.
public static void main(String[] args) {
List
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
true0x2.4. Reducing
The reduce() method combines stream elements. Two forms exist:
Optional
reduce(BinaryOperator
accumulator)No identity value; returns an Optional .
T reduce(T identity, BinaryOperator
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
list = Arrays.asList(ints);
Optional
optional = list.stream().reduce((a, b) -> a + b);
Optional
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
120x3. Converting Streams
Use collect() to transform a stream back into a collection or other data structures.
public static void main(String[] args) {
List
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
list1 = list.stream().map(String::length).collect(Collectors.toList());
List
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
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
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
empty = Optional.empty();
System.out.println(empty); // Optional.empty
Optional
opt = Optional.of("id10t.");
System.out.println(opt); // Optional[id10t.]
String name = "id10t.";
Optional
optOrNull = Optional.ofNullable(name);
System.out.println(optOrNull); // Optional[id10t.]
optOrNull = Optional.ofNullable(null);
System.out.println(optOrNull); // Optional.empty0x2. Checking Presence
Use isPresent() (or Java 11+ isEmpty() ) to test whether a value exists.
Optional
opt = Optional.of("id10t.");
System.out.println(opt.isPresent()); // true
Optional
optOrNull = Optional.ofNullable(null);
System.out.println(optOrNull.isPresent()); // false0x3. Non‑Null Expressions
ifPresent() executes a consumer when the value is present; Java 9+ adds ifPresentOrElse() for both present and absent cases.
Optional
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
opt = Optional.ofNullable(password);
System.out.println(opt.filter(pwd -> pwd.length() > 6).isPresent()); // false
Predicate
len6 = pwd -> pwd.length() > 6;
Predicate
len10 = pwd -> pwd.length() < 10;
password = "1234567";
opt = Optional.ofNullable(password);
boolean result = opt.filter(len6.and(len10)).isPresent();
System.out.println(result); // true0x6. Transforming Values
map() converts the contained value to another type, producing a new Optional .
String name = "id10t.";
Optional
nameOptional = Optional.of(name);
Optional
intOpt = nameOptional.map(String::length);
System.out.println(intOpt.orElse(0)); // 7Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.