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<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
30x2.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
true0x2.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
120x3. 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.empty0x2. 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()); // false0x3. 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); // true0x6. 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)); // 7Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected 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.
