1Z0-830 Streams and Lambdas - Java SE 21 Certification Prep
Functional Interfaces
A functional interface has exactly one abstract method.
// Defining a functional interface
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
// Default and static methods allowed
default void log() { }
static void helper() { }
}
// Common built-in functional interfaces:
// Predicate<T> - test condition, returns boolean
Predicate<String> isEmpty = s -> s.isEmpty();
boolean result = isEmpty.test(""); // true
// Function<T, R> - transform T to R
Function<String, Integer> length = s -> s.length();
int len = length.apply("hello"); // 5
// Consumer<T> - accept T, return nothing
Consumer<String> printer = s -> System.out.println(s);
printer.accept("hello");
// Supplier<T> - supply T (no input)
Supplier<Double> random = () -> Math.random();
double value = random.get();
// UnaryOperator<T> - T to T (extends Function)
UnaryOperator<String> upper = s -> s.toUpperCase();
// BinaryOperator<T> - (T, T) to T (extends BiFunction)
BinaryOperator<Integer> add = (a, b) -> a + b;
// BiPredicate, BiFunction, BiConsumer - two parameters
BiPredicate<String, Integer> lengthCheck = (s, len) -> s.length() > len;
BiFunction<String, String, String> concat = (a, b) -> a + b;
BiConsumer<String, Integer> print = (s, n) -> System.out.println(s + n);
Exam Tip: Memorize: Predicate (test), Function (apply), Consumer (accept), Supplier (get). The primitive versions use specific method names: IntPredicate, LongFunction, DoubleConsumer.
Lambda Expressions
// Lambda syntax
// (parameters) -> expression
// (parameters) -> { statements; }
// Single parameter (parentheses optional)
x -> x * 2
(x) -> x * 2
// Multiple parameters
(a, b) -> a + b
// No parameters
() -> System.out.println("Hello")
// With explicit types
(String s) -> s.length()
(int a, int b) -> a + b
// Block body with return
(int a, int b) -> {
int sum = a + b;
return sum;
}
// Variable capture (effectively final)
int factor = 2; // Must be effectively final
Function<Integer, Integer> multiply = n -> n * factor;
// factor = 3; // Compile error - variable used in lambda
// var in lambda parameters (Java 11+)
BiFunction<String, String, String> concat = (var a, var b) -> a + b;
// var with annotations
(@NonNull var a, @NotEmpty var b) -> a + b
Method References
// Four types of method references: // 1. Static method reference // ClassName::staticMethod Function<String, Integer> parser = Integer::parseInt; // Same as: s -> Integer.parseInt(s) // 2. Instance method on specific object // instance::instanceMethod String prefix = "Hello "; Function<String, String> greeter = prefix::concat; // Same as: s -> prefix.concat(s) // 3. Instance method on parameter // ClassName::instanceMethod Function<String, Integer> length = String::length; // Same as: s -> s.length() BiFunction<String, String, Boolean> startsWith = String::startsWith; // Same as: (s, prefix) -> s.startsWith(prefix) // 4. Constructor reference // ClassName::new Supplier<ArrayList> listFactory = ArrayList::new; // Same as: () -> new ArrayList() Function<Integer, ArrayList> sizedList = ArrayList::new; // Same as: size -> new ArrayList(size) // Array constructor reference Function<Integer, String[]> arrayFactory = String[]::new; // Same as: size -> new String[size]
Exam Tip: Method reference
String::length works because the String instance becomes the first parameter. strings.stream().map(String::length) calls length() on each string.
Stream Creation
// From collection
List<String> list = List.of("a", "b", "c");
Stream<String> stream = list.stream();
// From array
String[] array = {"a", "b", "c"};
Stream<String> arrStream = Arrays.stream(array);
// Stream.of()
Stream<String> ofStream = Stream.of("a", "b", "c");
// Infinite streams
Stream<Integer> infinite = Stream.iterate(0, n -> n + 1);
Stream<Double> randoms = Stream.generate(Math::random);
// Bounded iterate (Java 9+)
Stream<Integer> bounded = Stream.iterate(0, n -> n < 10, n -> n + 1);
// Stream.ofNullable (Java 9+)
Stream<String> nullable = Stream.ofNullable(getValue()); // Empty if null
// Primitive streams
IntStream ints = IntStream.of(1, 2, 3);
IntStream range = IntStream.range(1, 10); // 1 to 9
IntStream rangeClosed = IntStream.rangeClosed(1, 10); // 1 to 10
LongStream longs = LongStream.of(1L, 2L, 3L);
DoubleStream doubles = DoubleStream.of(1.0, 2.0);
// Empty stream
Stream<String> empty = Stream.empty();
// Concatenating streams
Stream<String> combined = Stream.concat(stream1, stream2);
Intermediate Operations
// Intermediate operations return a stream and are lazy
List<String> list = List.of("apple", "banana", "cherry", "date");
// filter - keep elements matching predicate
list.stream()
.filter(s -> s.length() > 5) // banana, cherry
// map - transform elements
list.stream()
.map(String::toUpperCase) // APPLE, BANANA...
// flatMap - flatten nested structures
List<List<Integer>> nested = List.of(List.of(1, 2), List.of(3, 4));
nested.stream()
.flatMap(Collection::stream) // 1, 2, 3, 4
// distinct - remove duplicates
Stream.of(1, 2, 2, 3, 3, 3)
.distinct() // 1, 2, 3
// sorted - natural order or with comparator
list.stream().sorted()
list.stream().sorted(Comparator.reverseOrder())
list.stream().sorted(Comparator.comparing(String::length))
// limit - take first n elements
Stream.iterate(1, n -> n + 1)
.limit(5) // 1, 2, 3, 4, 5
// skip - skip first n elements
list.stream().skip(2) // cherry, date
// peek - debug without modifying
list.stream()
.peek(System.out::println)
.map(String::toUpperCase)
.collect(Collectors.toList());
// takeWhile / dropWhile (Java 9+)
Stream.of(1, 2, 3, 4, 5, 1)
.takeWhile(n -> n < 4) // 1, 2, 3 (stops at first false)
Stream.of(1, 2, 3, 4, 5)
.dropWhile(n -> n < 4) // 4, 5
Terminal Operations
// Terminal operations trigger processing and produce result
List<String> list = List.of("apple", "banana", "cherry");
// forEach - perform action on each
list.stream().forEach(System.out::println);
// toArray - convert to array
String[] array = list.stream().toArray(String[]::new);
// collect - gather into collection
List<String> newList = list.stream().collect(Collectors.toList());
Set<String> set = list.stream().collect(Collectors.toSet());
// count - number of elements
long count = list.stream().filter(s -> s.length() > 5).count();
// min / max - with comparator
Optional<String> min = list.stream().min(Comparator.naturalOrder());
Optional<String> max = list.stream().max(Comparator.naturalOrder());
// findFirst / findAny - get element
Optional<String> first = list.stream().findFirst();
Optional<String> any = list.stream().findAny();
// anyMatch / allMatch / noneMatch - test predicates
boolean anyLong = list.stream().anyMatch(s -> s.length() > 5);
boolean allShort = list.stream().allMatch(s -> s.length() < 10);
boolean nonEmpty = list.stream().noneMatch(String::isEmpty);
// reduce - combine elements
Optional<String> concat = list.stream().reduce((a, b) -> a + b);
String withIdentity = list.stream().reduce("", (a, b) -> a + b);
// Primitive stream operations
IntStream.of(1, 2, 3, 4, 5).sum(); // 15
IntStream.of(1, 2, 3, 4, 5).average(); // OptionalDouble[3.0]
IntStream.of(1, 2, 3, 4, 5).max(); // OptionalInt[5]
IntStream.of(1, 2, 3, 4, 5).summaryStatistics();
Collectors
// Basic collectors
List<String> toList = stream.collect(Collectors.toList());
Set<String> toSet = stream.collect(Collectors.toSet());
LinkedList<String> toLinkd = stream.collect(Collectors.toCollection(LinkedList::new));
// Joining strings
String joined = list.stream().collect(Collectors.joining()); // abc
String delim = list.stream().collect(Collectors.joining(", ")); // a, b, c
String prefix = list.stream().collect(Collectors.joining(", ", "[", "]")); // [a, b, c]
// Counting
Long count = stream.collect(Collectors.counting());
// Summing / Averaging
Integer sum = stream.collect(Collectors.summingInt(String::length));
Double avg = stream.collect(Collectors.averagingInt(String::length));
// Min / Max
Optional<String> max = stream.collect(Collectors.maxBy(Comparator.naturalOrder()));
// Grouping
Map<Integer, List<String>> byLength =
list.stream().collect(Collectors.groupingBy(String::length));
// {5=[apple], 6=[banana, cherry]}
// Grouping with downstream collector
Map<Integer, Long> countByLength =
list.stream().collect(Collectors.groupingBy(String::length, Collectors.counting()));
// Partitioning (two groups: true/false)
Map<Boolean, List<String>> partition =
list.stream().collect(Collectors.partitioningBy(s -> s.length() > 5));
// {false=[apple], true=[banana, cherry]}
// toMap
Map<String, Integer> map = list.stream()
.collect(Collectors.toMap(Function.identity(), String::length));
// toMap with merge function (for duplicates)
Map<Integer, String> map2 = list.stream()
.collect(Collectors.toMap(String::length, Function.identity(),
(existing, replacement) -> existing));
// Teeing (Java 12+) - two collectors combined
record MinMax(String min, String max) {}
MinMax result = list.stream().collect(Collectors.teeing(
Collectors.minBy(Comparator.naturalOrder()),
Collectors.maxBy(Comparator.naturalOrder()),
(min, max) -> new MinMax(min.orElse(""), max.orElse(""))
));
Optional
// Creating Optional
Optional<String> full = Optional.of("hello"); // NullPointerException if null
Optional<String> nullable = Optional.ofNullable(getValue()); // Empty if null
Optional<String> empty = Optional.empty();
// Checking and getting
if (opt.isPresent()) {
String value = opt.get(); // Throws NoSuchElementException if empty
}
boolean isEmpty = opt.isEmpty(); // Java 11+
// Getting with fallback
String value = opt.orElse("default");
String lazy = opt.orElseGet(() -> expensiveDefault());
String orThrow = opt.orElseThrow(); // NoSuchElementException
String custom = opt.orElseThrow(() -> new CustomException());
// Conditional actions
opt.ifPresent(System.out::println);
opt.ifPresentOrElse(
System.out::println,
() -> System.out.println("Empty")
);
// Transforming
Optional<Integer> length = opt.map(String::length);
Optional<String> upper = opt.filter(s -> s.length() > 3).map(String::toUpperCase);
// flatMap for nested Optional
Optional<Optional<String>> nested = Optional.of(Optional.of("hi"));
Optional<String> flat = opt.flatMap(o -> o); // Avoid nesting
// or() - provide alternative Optional (Java 9+)
Optional<String> result = opt.or(() -> Optional.of("fallback"));
// stream() - convert to Stream (Java 9+)
List<String> list = optList.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
Exam Tip: Never use Optional.get() without checking isPresent(). Prefer orElse(), orElseGet(), or orElseThrow(). Optional should not be used as method parameters or class fields.
1Z0-830 Java SE 21 Certification - Table of Contents
Master all exam topics with comprehensive study guides and practice examples.
Popular Posts
1Z0-830 Java SE 21 Developer Certification
Azure AI Foundry Hello World
Azure AI Agent Hello World
Foundry vs Hub Projects
Build Agents with SDK
Bing Web Search Agent
Function Calling Agent
Spring Boot + Azure Key Vault Hello World Example
Spring Boot + Elasticsearch + Azure Key Vault Example
Spring Boot Azure AD (Entra ID) OAuth 2.0 Authentication
Deploy Spring Boot App to Azure App Service
Secure Azure App Service using Azure API Management
Deploy Spring Boot JAR to Azure App Service
Deploy Spring Boot + MySQL to Azure App Service
Spring Boot + Azure Managed Identity Example
Secure Spring Boot Azure Web App with Managed Identity + App Registration
Elasticsearch 8 Security - Integrate Azure AD OIDC