0% found this document useful (0 votes)
9 views54 pages

Java Streams Functional Programming

This document serves as a comprehensive guide on Java Streams and Functional Programming, covering topics from basic to advanced levels. It includes detailed explanations of functional programming principles, lambda expressions, functional interfaces, and various Java 8 features like the Stream API. Additionally, it provides practical examples, interview preparation tips, and common pitfalls to avoid for developers with 3+ years of experience.

Uploaded by

Tushar
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views54 pages

Java Streams Functional Programming

This document serves as a comprehensive guide on Java Streams and Functional Programming, covering topics from basic to advanced levels. It includes detailed explanations of functional programming principles, lambda expressions, functional interfaces, and various Java 8 features like the Stream API. Additionally, it provides practical examples, interview preparation tips, and common pitfalls to avoid for developers with 3+ years of experience.

Uploaded by

Tushar
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Java Streams &

Functional Programming

From Basic to Advanced

Complete Interview Preparation Guide

For 3+ Year Experienced Developers


Table of Contents
1. Introduction to Functional Programming
2. Lambda Expressions - The Foundation
3. Functional Interfaces in Detail
4. Method References
5. Stream API Fundamentals
6. Intermediate Operations
7. Terminal Operations
8. Collectors - The Power Tools
9. Advanced Stream Patterns
10. Parallel Streams and Performance
11. Optional Class - Null Safety
12. Real-world Interview Problems
13. Common Pitfalls and Best Practices
1. Introduction to Functional Programming
Functional Programming (FP) is a programming paradigm that treats computation as the evaluation of
mathematical functions and avoids changing state and mutable data. Java 8 introduced functional programming
features including lambda expressions, streams, and functional interfaces.

Key Principles of Functional Programming:


Immutability: Data doesn't change after creation
Pure Functions: Same input always produces same output, no side effects
First-Class Functions: Functions can be passed as arguments and returned
Higher-Order Functions: Functions that take or return other functions
Declarative Style: Focus on WHAT to do, not HOW to do it

Imperative vs Functional - Example:


import [Link].*;
import [Link].*;
public class ImperativeVsFunctional {
public static void main(String[] args) {
List<Integer> numbers = [Link](1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// IMPERATIVE APPROACH (Traditional)
List<Integer> evenSquares = new ArrayList<>();
for(Integer num : numbers) {
if(num % 2 == 0) { // Filter even
int square = num * num; // Transform
[Link](square);
}
}
[Link]("Imperative: " + evenSquares);
// FUNCTIONAL APPROACH (Java 8+)
List<Integer> evenSquaresFunctional = [Link]()
.filter(n -> n % 2 == 0) // Filter even
.map(n -> n * n) // Transform
.collect([Link]()); // Collect
[Link]("Functional: " + evenSquaresFunctional);
// Both output: [4, 16, 36, 64, 100]
}
}
Benefits of Functional Approach:
1. More readable and concise
2. Less error-prone (no manual list management)
3. Easier to parallelize
4. Composable and reusable
5. Declarative - expresses intent clearly
2. Lambda Expressions - The Foundation
Lambda expressions are anonymous functions that can be passed around as values. They provide a clear and
concise way to represent one method interface using an expression.

2.1 Lambda Syntax


Lambda Syntax Variations:
// 1. No parameters
() -> [Link]("Hello")
() -> 42
() -> { return "Hello"; }
// 2. Single parameter (parentheses optional)
x -> x * x
(x) -> x * x
x -> { return x * x; }
// 3. Multiple parameters
(x, y) -> x + y
(x, y) -> { return x + y; }
(String s, int n) -> [Link]() + n // Explicit types
// 4. Complex body (requires braces and return)
(a, b) -> {
int sum = a + b;
int product = a * b;
return sum + product;
}

2.2 Lambda Examples


import [Link].*;
import [Link].*;
public class LambdaExamples {
public static void main(String[] args) {
// Example 1: Runnable
// Old way
Runnable r1 = new Runnable() {
@Override
public void run() {
[Link]("Hello from thread");
}
};
// Lambda way
Runnable r2 = () -> [Link]("Hello from thread");
new Thread(r2).start();
// Example 2: Comparator
List<String> names = [Link]("John", "Alice", "Bob", "Charlie");
// Old way
[Link](names, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return [Link]() - [Link]();
}
});
// Lambda way
[Link](names, (s1, s2) -> [Link]() - [Link]());
// Even better with [Link]
[Link](names, [Link](String::length));
[Link]("Sorted by length: " + names);
// Example 3: Custom Functional Interface
Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;
Calculator divide = (a, b) -> {
if(b == 0) throw new ArithmeticException("Division by zero");
return a / b;
};
[Link]("Add: " + [Link](10, 5)); // 15
[Link]("Multiply: " + [Link](10, 5)); // 50
[Link]("Divide: " + [Link](10, 5)); // 2
// Example 4: forEach
List<Integer> numbers = [Link](1, 2, 3, 4, 5);
// Old way
for(Integer n : numbers) {
[Link](n);
}
// Lambda way
[Link](n -> [Link](n));
// Method reference (we'll cover this later)
[Link]([Link]::println);
// Example 5: Variable Capture
int multiplier = 10; // Must be effectively final
Function<Integer, Integer> multiplyByTen =
x -> x * multiplier;
[Link]("5 * 10 = " + [Link](5));
// multiplier = 20; // ERROR! Cannot modify captured variable
}
}
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
}
// Output:
// Sorted by length: [Bob, John, Alice, Charlie]
// Add: 15
// Multiply: 50
// Divide: 2
3. Functional Interfaces in Detail
A functional interface is an interface with exactly one abstract method (SAM - Single Abstract Method). The
@FunctionalInterface annotation is optional but recommended for clarity and compile-time checking.

3.1 Built-in Functional Interfaces


Interface Method Use Case Example

Predicate<T> boolean test(T t) Condition/Filter n -> n > 0

Function<T,R> R apply(T t) Transform s -> [Link]()

Consumer<T> void accept(T t) Action/Side-effect s -> print(s)

Supplier<T> T get() Generate/Provide () -> new User()

BiPredicate<T,U> boolean test(T t, U u) Two-arg condition (x,y) -> x>y

BiFunction<T,U,R> R apply(T t, U u) Two-arg transform (x,y) -> x+y

BiConsumer<T,U> void accept(T t, U u) Two-arg action (k,v) -> [Link](k,v)

UnaryOperator<T> T apply(T t) Same type I/O n -> n * 2

BinaryOperator<T> T apply(T t1, T t2) Same type 2 inputs (a,b) -> a+b

import [Link].*;
import [Link].*;
public class FunctionalInterfaceExamples {
public static void main(String[] args) {
// 1. Predicate<T> - Returns boolean
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<Integer> isPositive = n -> n > 0;
Predicate<String> isLongString = s -> [Link]() > 5;
[Link]("Is 4 even? " + [Link](4));
[Link]("Is 5 even? " + [Link](5));
// Combining Predicates
Predicate<Integer> isEvenAndPositive = [Link](isPositive);
Predicate<Integer> isEvenOrPositive = [Link](isPositive);
Predicate<Integer> isOdd = [Link]();
[Link]("Is 4 even AND positive? " +
[Link](4));
[Link]("Is -2 even AND positive? " +
[Link](-2));
// 2. Function<T, R> - Transforms input to output
Function<String, Integer> stringLength = s -> [Link]();
Function<Integer, Integer> square = n -> n * n;
Function<String, String> uppercase = s -> [Link]();
[Link]("Length of 'Hello': " +
[Link]("Hello"));
[Link]("Square of 5: " + [Link](5));
// Composing Functions
Function<String, Integer> lengthSquared =
[Link](square);
[Link]("Length of 'Hi' squared: " +
[Link]("Hi")); // (2)^2 = 4
Function<Integer, String> intToString = n -> [Link](n);
Function<String, Integer> parseAndSquare =
[Link](square); // square first, then toString
// 3. Consumer<T> - Accepts input, returns nothing
Consumer<String> printer = s -> [Link](s);
Consumer<List<Integer>> listPrinter =
list -> [Link]([Link]::println);
[Link]("Hello World");
// Chaining Consumers
Consumer<String> upperPrinter =
s -> [Link]([Link]());
Consumer<String> combinedPrinter =
[Link](upperPrinter);
[Link]("test"); // Prints: test, then TEST
// 4. Supplier<T> - Provides/generates values
Supplier<Double> randomSupplier = () -> [Link]();
Supplier<String> dateSupplier =
() -> new [Link]().toString();
Supplier<List<String>> listSupplier = () -> new ArrayList<>();
[Link]("Random: " + [Link]());
[Link]("Date: " + [Link]());
// 5. BiFunction<T, U, R> - Two inputs, one output
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
BiFunction<String, Integer, String> repeat =
(s, n) -> [Link](n);
[Link]("5 + 3 = " + [Link](5, 3));
[Link]("Repeat: " + [Link]("Hi", 3));
// 6. UnaryOperator<T> - Special Function<T, T>
UnaryOperator<Integer> doubleIt = n -> n * 2;
UnaryOperator<String> addExclamation = s -> s + "!";
[Link]("Double 5: " + [Link](5));
[Link]("Add !: " + [Link]("Hello"));
// 7. BinaryOperator<T> - Special BiFunction<T, T, T>
BinaryOperator<Integer> multiply = (a, b) -> a * b;
BinaryOperator<String> concat = (s1, s2) -> s1 + s2;
[Link]("3 * 4 = " + [Link](3, 4));
[Link]("Concat: " + [Link]("Hello", "World"));
// 8. Primitive Specializations (avoid boxing/unboxing)
IntPredicate isEvenInt = n -> n % 2 == 0;
IntFunction<String> intToStr = n -> "Number: " + n;
IntConsumer printInt = n -> [Link](n);
IntSupplier randomInt = () -> (int)([Link]() * 100);
IntUnaryOperator tripleIt = n -> n * 3;
IntBinaryOperator addInts = (a, b) -> a + b;
[Link]("Is 6 even? " + [Link](6));
[Link]("Random int: " + [Link]());
}
}
3.2 Creating Custom Functional Interfaces
import [Link].*;
// Custom functional interfaces
@FunctionalInterface
interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
@FunctionalInterface
interface Validator<T> {
boolean validate(T t);
// Default methods are allowed
default Validator<T> and(Validator<T> other) {
return t -> [Link](t) && [Link](t);
}
default Validator<T> or(Validator<T> other) {
return t -> [Link](t) || [Link](t);
}
}
@FunctionalInterface
interface StringProcessor {
String process(String input);
default StringProcessor andThen(StringProcessor after) {
return input -> [Link]([Link](input));
}
}
public class CustomFunctionalInterfaces {
public static void main(String[] args) {
// Using TriFunction
TriFunction<Integer, Integer, Integer, Integer> sumOfThree =
(a, b, c) -> a + b + c;
[Link]("Sum: " + [Link](1, 2, 3));
// Using Validator
Validator<String> notNull = s -> s != null;
Validator<String> notEmpty = s -> ![Link]();
Validator<String> hasMinLength =
s -> [Link]() >= 3;
Validator<String> fullValidator =
[Link](notEmpty).and(hasMinLength);
[Link]("Valid 'Hello'? " +
[Link]("Hello")); // true
[Link]("Valid 'Hi'? " +
[Link]("Hi")); // false
[Link]("Valid null? " +
[Link](null)); // false
// Using StringProcessor
StringProcessor trim = s -> [Link]();
StringProcessor uppercase = s -> [Link]();
StringProcessor addPrefix = s -> ">> " + s;
StringProcessor pipeline =
[Link](uppercase).andThen(addPrefix);
[Link]([Link](" hello "));
// Output: >> HELLO
// Real-world example: Form validation
Validator<User> emailValid =
u -> [Link]() != null && [Link]().contains("@");
Validator<User> ageValid =
u -> [Link]() >= 18;
Validator<User> nameValid =
u -> [Link]() != null && [Link]().length() >= 2;
Validator<User> userValidator =
[Link](ageValid).and(nameValid);
User user1 = new User("John", 25, "john@[Link]");
User user2 = new User("A", 16, "invalid");
[Link]("User1 valid? " +
[Link](user1)); // true
[Link]("User2 valid? " +
[Link](user2)); // false
}
}
class User {
private String name;
private int age;
private String email;
public User(String name, int age, String email) {
[Link] = name;
[Link] = age;
[Link] = email;
}
public String getName() { return name; }
public int getAge() { return age; }
public String getEmail() { return email; }
}
4. Method References
Method references are shorthand notation for lambda expressions that simply call an existing method. They
make code more readable and concise when a lambda expression does nothing except call an existing method.

4.1 Types of Method References


Four Types of Method References:
1. Reference to a Static Method
ClassName::staticMethodName
Example: Integer::parseInt
Lambda equivalent: s -> [Link](s)
2. Reference to an Instance Method of a Particular Object
objectReference::instanceMethodName
Example: [Link]::println
Lambda equivalent: x -> [Link](x)
3. Reference to an Instance Method of an Arbitrary Object
ClassName::instanceMethodName
Example: String::toLowerCase
Lambda equivalent: s -> [Link]()
4. Reference to a Constructor
ClassName::new
Example: ArrayList::new
Lambda equivalent: () -> new ArrayList()

import [Link].*;
import [Link].*;
import [Link].*;
public class MethodReferenceExamples {
public static void main(String[] args) {
List<String> names = [Link]("Alice", "Bob", "Charlie", "David");
// 1. Static Method Reference
// Lambda
Function<String, Integer> parseInt1 = s -> [Link](s);
// Method reference
Function<String, Integer> parseInt2 = Integer::parseInt;
[Link]([Link]("123"));
// More examples
List<String> numbers = [Link]("1", "2", "3", "4", "5");
List<Integer> integers = [Link]()
.map(Integer::parseInt) // Static method reference
.collect([Link]());
[Link]("Parsed: " + integers);
// 2. Instance Method Reference (Particular Object)
// Lambda
Consumer<String> print1 = s -> [Link](s);
// Method reference
Consumer<String> print2 = [Link]::println;
[Link]([Link]::println);
// Custom object example
Printer printer = new Printer();
[Link](printer::print); // Instance method of 'printer'
// 3. Instance Method Reference (Arbitrary Object)
// Lambda
Function<String, String> upper1 = s -> [Link]();
// Method reference
Function<String, String> upper2 = String::toUpperCase;
List<String> upperNames = [Link]()
.map(String::toUpperCase) // Called on each string
.collect([Link]());
[Link]("Upper: " + upperNames);
// More examples
List<String> sorted = [Link]()
.sorted(String::compareToIgnoreCase)
.collect([Link]());
// Comparator using method reference
[Link](String::compareToIgnoreCase);
// 4. Constructor Reference
// Lambda
Supplier<List<String>> listSupplier1 = () -> new ArrayList<>();
// Constructor reference
Supplier<List<String>> listSupplier2 = ArrayList::new;
List<String> newList = [Link]();
// Creating objects from stream
List<Person> people = [Link]()
.map(Person::new) // Constructor reference
.collect([Link]());
[Link]([Link]::println);
// Array constructor reference
Function<Integer, String[]> arrayCreator = String[]::new;
String[] stringArray = [Link](5);
[Link]("Array length: " + [Link]);
// Stream to array using constructor reference
String[] nameArray = [Link]()
.toArray(String[]::new);
// 5. Complex Examples
// BiFunction with constructor
BiFunction<String, Integer, Person> personCreator =
Person::new;
Person person = [Link]("John", 30);
// Chaining method references
List<Integer> lengths = [Link]()
.map(String::trim)
.map(String::length)
.collect([Link]());
[Link]("Lengths: " + lengths);
// Method reference in reduce
Optional<String> concatenated = [Link]()
.reduce(String::concat);
[Link]("Concatenated: " + [Link](""));
// Using method reference with Predicate
List<String> nonEmpty = [Link]()
.filter(((Predicate<String>) String::isEmpty).negate())
.collect([Link]());
}
}
class Printer {
public void print(String s) {
[Link]("Printing: " + s);
}
}
class Person {
private String name;
private int age;
public Person(String name) {
[Link] = name;
[Link] = 0;
}
public Person(String name, int age) {
[Link] = name;
[Link] = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
5. Stream API Fundamentals
A Stream is a sequence of elements supporting sequential and parallel aggregate operations. Streams don't
store data; they operate on the source data structure and produce a result.

5.1 Stream Characteristics


Not a data structure: Streams don't store elements; they compute on demand
Functional in nature: Operations produce results but don't modify source
Lazy evaluation: Intermediate operations are not executed until terminal operation
Possibly unbounded: Can be infinite (e.g., [Link]())
Consumable: Can be traversed only once, like an Iterator

5.2 Creating Streams


import [Link].*;
import [Link].*;
import [Link].*;
import [Link].*;
public class CreatingStreams {
public static void main(String[] args) {
// 1. From Collections
List<String> list = [Link]("a", "b", "c");
Stream<String> stream1 = [Link]();
Stream<String> parallelStream = [Link]();
// 2. From Arrays
String[] array = {"a", "b", "c"};
Stream<String> stream2 = [Link](array);
Stream<String> stream3 = [Link](array, 1, 3); // From index 1 to 2
// 3. Using [Link]()
Stream<String> stream4 = [Link]("a", "b", "c");
Stream<Integer> stream5 = [Link](1, 2, 3, 4, 5);
// 4. [Link]()
Stream<String> stream6 = Stream.<String>builder()
.add("a")
.add("b")
.add("c")
.build();
// 5. Empty Stream
Stream<String> emptyStream = [Link]();
// 6. Infinite Streams
// [Link]() - generates infinite stream
Stream<Integer> infiniteStream1 = [Link](0, n -> n + 2);
// Use limit() to make it finite
List<Integer> first10Evens = infiniteStream1
.limit(10)
.collect([Link]());
[Link]("First 10 evens: " + first10Evens);
// With predicate (Java 9+)
Stream<Integer> limitedStream = [Link](0, n -> n < 20, n -> n + 2);
// [Link]() - generates infinite stream
Stream<Double> randomStream = [Link](Math::random);
List<Double> randomNumbers = randomStream
.limit(5)
.collect([Link]());
[Link]("Random numbers: " + randomNumbers);
// 7. Primitive Streams (avoid boxing)
IntStream intStream = [Link](1, 6); // 1,2,3,4,5
IntStream intStream2 = [Link](1, 5); // 1,2,3,4,5
LongStream longStream = [Link](1L, 2L, 3L);
DoubleStream doubleStream = [Link](1.0, 2.0, 3.0);
// Random number streams
IntStream randomInts = new Random().ints(5, 0, 100); // 5 random ints [0,100)
// 8. From String
IntStream charStream = "Hello".chars();
[Link](c -> [Link]((char)c + " "));
[Link]();
Stream<String> lineStream = "Hello\nWorld\nJava".lines();
// 9. From Files
try {
Stream<String> lines = [Link]([Link]("[Link]"));
// [Link]([Link]::println);
} catch(IOException e) {
// Handle exception
}
// 10. Concatenating Streams
Stream<String> stream7 = [Link]("a", "b");
Stream<String> stream8 = [Link]("c", "d");
Stream<String> concatenated = [Link](stream7, stream8);
// 11. From Map
Map<String, Integer> map = new HashMap<>();
[Link]("a", 1);
[Link]("b", 2);
Stream<[Link]<String, Integer>> entryStream =
[Link]().stream();
Stream<String> keyStream = [Link]().stream();
Stream<Integer> valueStream = [Link]().stream();
// 12. Using StreamSupport (for Iterators and Spliterators)
Iterator<String> iterator = [Link]("a", "b").iterator();
Stream<String> streamFromIterator = [Link](
[Link](iterator,
[Link]),
false
);
}
}
5.3 Stream Pipeline Structure
Stream Pipeline = Source → Intermediate Operations → Terminal Operation
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■ Source Intermediate Ops Terminal Op ■
■ ■■■■■■ ■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■ ■
■ Collection → filter, map, → collect, forEach, ■
■ Array sorted, distinct count, reduce ■
■ [Link]() ■
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
Key Points:
1. Intermediate operations are LAZY (not executed immediately)
2. Terminal operation triggers execution
3. Stream can only be used once
4. Operations are pipelined for efficiency

import [Link].*;
import [Link].*;
public class StreamPipeline {
public static void main(String[] args) {
List<String> names = [Link](
"Alice", "Bob", "Charlie", "David", "Eve",
"Frank", "Grace", "Henry"
);
// Example 1: Basic Pipeline
List<String> result = [Link]() // Source
.filter(s -> [Link]() > 4) // Intermediate
.map(String::toUpperCase) // Intermediate
.sorted() // Intermediate
.collect([Link]()); // Terminal
[Link]("Result: " + result);
// Example 2: Demonstrating Lazy Evaluation
[Link]("\nDemonstrating lazy evaluation:");
Stream<String> stream = [Link]()
.filter(s -> {
[Link]("Filtering: " + s);
return [Link]() > 4;
})
.map(s -> {
[Link]("Mapping: " + s);
return [Link]();
});
[Link]("Stream created but not executed yet!");
[Link]("Now executing with terminal operation:");
List<String> collected = [Link]([Link]());
// Only now the pipeline executes!
// Example 3: Stream can only be used once
Stream<Integer> numbers = [Link](1, 2, 3, 4, 5);
long count = [Link](); // Terminal operation
[Link]("Count: " + count);
// [Link]([Link]::println);
// ERROR! Stream has already been operated upon
// Example 4: Short-circuiting
[Link]("\nShort-circuiting example:");
Optional<String> firstLong = [Link]()
.filter(s -> {
[Link]("Checking: " + s);
return [Link]() > 5;
})
.findFirst(); // Stops after first match!
[Link]("First long name: " + [Link]("None"));
// Example 5: Parallel Stream
[Link]("\nParallel stream:");
List<Integer> largeList = [Link](1, 1000)
.boxed()
.collect([Link]());
// Sequential
long start = [Link]();
[Link]()
.map(n -> n * n)
.reduce(0, Integer::sum);
long seqTime = [Link]() - start;
// Parallel
start = [Link]();
[Link]()
.map(n -> n * n)
.reduce(0, Integer::sum);
long parTime = [Link]() - start;
[Link]("Sequential: " + seqTime + "ms");
[Link]("Parallel: " + parTime + "ms");
}
}
6. Intermediate Operations
Intermediate operations return a new stream and are always lazy. They don't execute until a terminal operation is
called.

Operation Description Example

filter(Predicate) Keeps elements matching predicate filter(n -> n > 5)

map(Function) Transforms each element map(String::toUpperCase)

flatMap(Function) Flattens nested structures flatMap(List::stream)

distinct() Removes duplicates distinct()

sorted() Sorts elements sorted()

sorted(Comparator) Sorts with comparator sorted((a,b) -> b-a)

peek(Consumer) Performs action without changing peek([Link]::println)

limit(n) Truncates to n elements limit(10)

skip(n) Skips first n elements skip(5)

takeWhile(Predicate) Takes while condition true takeWhile(n -> n < 5)

dropWhile(Predicate) Drops while condition true dropWhile(n -> n < 5)


6.1 filter() - Filtering Elements
import [Link].*;
import [Link].*;
public class FilterExamples {
public static void main(String[] args) {
List<Integer> numbers = [Link](1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Simple filter
List<Integer> evens = [Link]()
.filter(n -> n % 2 == 0)
.collect([Link]());
[Link]("Evens: " + evens);
// Multiple filters (can chain)
List<Integer> filtered = [Link]()
.filter(n -> n % 2 == 0) // Even
.filter(n -> n > 5) // Greater than 5
.collect([Link]());
[Link]("Even and > 5: " + filtered);
// Complex predicate
List<String> names = [Link](
"Alice", "Bob", "Charlie", "David", "Eve"
);
List<String> longNames = [Link]()
.filter(s -> [Link]() > 4)
.filter(s -> [Link]("a") || [Link]("e"))
.collect([Link]());
// Filter with objects
List<Employee> employees = [Link](
new Employee("Alice", 30, 75000),
new Employee("Bob", 25, 65000),
new Employee("Charlie", 35, 85000),
new Employee("David", 28, 70000)
);
List<Employee> seniorHighEarners = [Link]()
.filter(e -> [Link]() > 28)
.filter(e -> [Link]() > 70000)
.collect([Link]());
[Link]([Link]::println);
// Filter null values
List<String> listWithNulls = [Link](
"a", null, "b", null, "c"
);
List<String> nonNulls = [Link]()
.filter(Objects::nonNull)
.collect([Link]());
// Filter by type
List<Object> mixed = [Link](1, "two", 3.0, "four", 5);
List<String> strings = [Link]()
.filter(obj -> obj instanceof String)
.map(obj -> (String) obj)
.collect([Link]());
[Link]("Strings: " + strings);
}
}
class Employee {
private String name;
private int age;
private double salary;
public Employee(String name, int age, double salary) {
[Link] = name;
[Link] = age;
[Link] = salary;
}
public String getName() { return name; }
public int getAge() { return age; }
public double getSalary() { return salary; }
@Override
public String toString() {
return [Link]("%s (Age: %d, Salary: %.0f)",
name, age, salary);
}
}
6.2 map() - Transforming Elements
import [Link].*;
import [Link].*;
public class MapExamples {
public static void main(String[] args) {
// Simple transformation
List<String> names = [Link]("alice", "bob", "charlie");
List<String> upperNames = [Link]()
.map(String::toUpperCase)
.collect([Link]());
[Link]("Upper: " + upperNames);
// Transform to different type
List<Integer> lengths = [Link]()
.map(String::length)
.collect([Link]());
[Link]("Lengths: " + lengths);
// Chaining maps
List<Integer> doubled = [Link](1, 2, 3, 4, 5).stream()
.map(n -> n * 2)
.map(n -> n + 1)
.collect([Link]());
[Link]("Doubled + 1: " + doubled);
// Object transformation
List<Employee> employees = [Link](
new Employee("Alice", 75000),
new Employee("Bob", 65000),
new Employee("Charlie", 85000)
);
// Extract names
List<String> employeeNames = [Link]()
.map(Employee::getName)
.collect([Link]());
// Give raise
List<Employee> raisedEmployees = [Link]()
.map(e -> new Employee([Link](), [Link]() * 1.1))
.collect([Link]());
[Link](e ->
[Link]([Link]() + ": " + [Link]()));
// MapToInt, MapToLong, MapToDouble (avoid boxing)
int totalLength = [Link]()
.mapToInt(String::length)
.sum();
[Link]("Total length: " + totalLength);
double averageSalary = [Link]()
.mapToDouble(Employee::getSalary)
.average()
.orElse(0.0);
[Link]("Average salary: " + averageSalary);
// Complex mapping
Map<String, Integer> nameToLength = [Link]()
.collect([Link](
name -> name,
String::length
));
[Link]("Name to length: " + nameToLength);
}
}
class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
[Link] = name;
[Link] = salary;
}
public String getName() { return name; }
public double getSalary() { return salary; }
}
6.3 flatMap() - Flattening Nested Structures
import [Link].*;
import [Link].*;
public class FlatMapExamples {
public static void main(String[] args) {
// Problem: List of Lists
List<List<Integer>> listOfLists = [Link](
[Link](1, 2, 3),
[Link](4, 5),
[Link](6, 7, 8, 9)
);
// Using map() - gives Stream<List<Integer>>
Stream<List<Integer>> streamOfLists = [Link]()
.map(list -> list); // Still nested!
// Using flatMap() - flattens to Stream<Integer>
List<Integer> flattened = [Link]()
.flatMap(List::stream) // Flattens nested lists
.collect([Link]());
[Link]("Flattened: " + flattened);
// Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]
// Example 2: Split strings into words
List<String> sentences = [Link](
"Hello World",
"Java Streams",
"Functional Programming"
);
// Get all words
List<String> words = [Link]()
.flatMap(sentence -> [Link]([Link](" ")))
.collect([Link]());
[Link]("Words: " + words);
// Get unique words
Set<String> uniqueWords = [Link]()
.flatMap(sentence -> [Link]([Link](" ")))
.map(String::toLowerCase)
.collect([Link]());
// Example 3: Objects with collections
List<Department> departments = [Link](
new Department("IT", [Link]("Alice", "Bob", "Charlie")),
new Department("HR", [Link]("David", "Eve")),
new Department("Finance", [Link]("Frank", "Grace"))
);
// Get all employees across all departments
List<String> allEmployees = [Link]()
.flatMap(dept -> [Link]().stream())
.collect([Link]());
[Link]("All employees: " + allEmployees);
// Example 4: flatMapToInt for primitives
List<String> numbers = [Link]("1,2,3", "4,5", "6,7,8");
int sum = [Link]()
.flatMapToInt(s -> [Link]([Link](","))
.mapToInt(Integer::parseInt))
.sum();
[Link]("Sum: " + sum);
// Example 5: Cartesian product using flatMap
List<String> colors = [Link]("Red", "Green", "Blue");
List<String> sizes = [Link]("S", "M", "L");
List<String> combinations = [Link]()
.flatMap(color ->
[Link]().map(size -> color + "-" + size))
.collect([Link]());
[Link]("Combinations: " + combinations);
// Output: [Red-S, Red-M, Red-L, Green-S, ...]
// Example 6: map vs flatMap comparison
[Link]("\nmap vs flatMap:");
// Using map - gives List<Stream<Integer>>
List<Stream<Integer>> withMap = [Link]()
.map(List::stream)
.collect([Link]());
// Can't use this easily!
// Using flatMap - gives List<Integer>
List<Integer> withFlatMap = [Link]()
.flatMap(List::stream)
.collect([Link]());
[Link]("With flatMap: " + withFlatMap);
// Example 7: Optional with flatMap
Optional<String> optional1 = [Link]("Hello");
Optional<String> optional2 = [Link]("World");
// Combining Optionals
Optional<String> combined = [Link](s1 ->
[Link](s2 -> s1 + " " + s2)
);
[Link]("Combined: " + [Link](""));
}
}
class Department {
private String name;
private List<String> employees;
public Department(String name, List<String> employees) {
[Link] = name;
[Link] = employees;
}
public String getName() { return name; }
public List<String> getEmployees() { return employees; }
}
6.4 Other Intermediate Operations
import [Link].*;
import [Link].*;
public class OtherIntermediateOps {
public static void main(String[] args) {
// 1. distinct() - Remove duplicates
List<Integer> numbers = [Link](1, 2, 2, 3, 3, 3, 4, 5, 5);
List<Integer> unique = [Link]()
.distinct()
.collect([Link]());
[Link]("Unique: " + unique);
// For custom objects, override equals() and hashCode()
List<Person> people = [Link](
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Alice", 25), // Duplicate
new Person("Charlie", 35)
);
List<Person> uniquePeople = [Link]()
.distinct()
.collect([Link]());
// 2. sorted() - Sort elements
List<Integer> unsorted = [Link](5, 2, 8, 1, 9);
List<Integer> sorted = [Link]()
.sorted()
.collect([Link]());
[Link]("Sorted: " + sorted);
// Reverse order
List<Integer> reversed = [Link]()
.sorted([Link]())
.collect([Link]());
// Sort objects
List<Person> sortedByAge = [Link]()
.sorted([Link](Person::getAge))
.collect([Link]());
// Multiple criteria
List<Person> sorted2 = [Link]()
.sorted([Link](Person::getName)
.thenComparingInt(Person::getAge))
.collect([Link]());
// 3. peek() - Perform action without modifying
// Useful for debugging
List<Integer> result = [Link]()
.filter(n -> n > 2)
.peek(n -> [Link]("Filtered: " + n))
.map(n -> n * 2)
.peek(n -> [Link]("Mapped: " + n))
.collect([Link]());
// 4. limit() - Limit stream size
List<Integer> first5 = [Link](1, n -> n + 1)
.limit(5)
.collect([Link]());
[Link]("First 5: " + first5);
// With filter
List<Integer> first3Evens = [Link](1, n -> n + 1)
.filter(n -> n % 2 == 0)
.limit(3)
.collect([Link]());
// 5. skip() - Skip elements
List<Integer> skip3 = [Link](1, 2, 3, 4, 5, 6, 7, 8)
.stream()
.skip(3)
.collect([Link]());
[Link]("Skip 3: " + skip3);
// Pagination
int pageSize = 3;
int pageNumber = 2; // 0-indexed
List<Integer> page = [Link]()
.skip(pageNumber * pageSize)
.limit(pageSize)
.collect([Link]());
// 6. takeWhile() - Take elements while condition is true
// (Java 9+)
List<Integer> taken = [Link](1, 2, 3, 4, 5, 1, 2)
.takeWhile(n -> n < 4)
.collect([Link]());
[Link]("TakeWhile < 4: " + taken); // [1, 2, 3]
// Stops at first element that doesn't match!
// 7. dropWhile() - Drop elements while condition is true
// (Java 9+)
List<Integer> dropped = [Link](1, 2, 3, 4, 5, 1, 2)
.dropWhile(n -> n < 4)
.collect([Link]());
[Link]("DropWhile < 4: " + dropped); // [4, 5, 1, 2]
// Difference between filter and takeWhile
List<Integer> filtered = [Link](1, 2, 3, 4, 5, 1, 2)
.filter(n -> n < 4)
.collect([Link]());
[Link]("Filter < 4: " + filtered); // [1, 2, 3, 1, 2]
// filter checks ALL elements!
// 8. Combining operations
List<String> names = [Link](
"Alice", "Bob", "Charlie", "David", "Eve",
"Frank", "Grace", "Henry", "Ivy", "Jack"
);
List<String> processed = [Link]()
.filter(s -> [Link]() > 3) // Filter short names
.distinct() // Remove duplicates
.sorted() // Sort
.map(String::toUpperCase) // Uppercase
.skip(1) // Skip first
.limit(5) // Take 5
.peek(s -> [Link]("Processing: " + s))
.collect([Link]());
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
[Link] = name;
[Link] = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != [Link]()) return false;
Person person = (Person) o;
return age == [Link] &&
[Link](name, [Link]);
}
@Override
public int hashCode() {
return [Link](name, age);
}
}
7. Terminal Operations
Terminal operations produce a result or side-effect and mark the end of a stream pipeline. After a terminal
operation, the stream is consumed and cannot be reused.

Operation Return Type Description

forEach(Consumer) void Performs action on each element

forEachOrdered(Consumer) void forEach in encounter order

toArray() Object[] Collects to array

reduce(BinaryOp) Optional<T> Reduces to single value

collect(Collector) R Mutable reduction

min(Comparator) Optional<T> Minimum element

max(Comparator) Optional<T> Maximum element

count() long Count of elements

anyMatch(Predicate) boolean Any element matches?

allMatch(Predicate) boolean All elements match?

noneMatch(Predicate) boolean No elements match?

findFirst() Optional<T> First element

findAny() Optional<T> Any element (non-deterministic)


import [Link].*;
import [Link].*;
public class TerminalOperations {
public static void main(String[] args) {
List<Integer> numbers = [Link](1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 1. forEach() - Perform action on each element
[Link]("forEach:");
[Link]()
.filter(n -> n % 2 == 0)
.forEach(n -> [Link](n + " "));
[Link]();
// forEach vs forEachOrdered (matters in parallel streams)
[Link]("\nParallel forEach (random order):");
[Link]()
.forEach(n -> [Link](n + " "));
[Link]("\nParallel forEachOrdered (maintains order):");
[Link]()
.forEachOrdered(n -> [Link](n + " "));
[Link]();
// 2. count() - Count elements
long count = [Link]()
.filter(n -> n > 5)
.count();
[Link]("\nCount > 5: " + count);
// 3. min() and max()
Optional<Integer> min = [Link]()
.min(Integer::compareTo);
Optional<Integer> max = [Link]()
.max(Integer::compareTo);
[Link]("Min: " + [Link](0));
[Link]("Max: " + [Link](0));
// For custom objects
List<Employee> employees = [Link](
new Employee("Alice", 75000),
new Employee("Bob", 65000),
new Employee("Charlie", 85000)
);
Optional<Employee> highestPaid = [Link]()
.max([Link](Employee::getSalary));
[Link](e ->
[Link]("Highest paid: " + [Link]()));
// 4. toArray() - Convert to array
Integer[] array = [Link]()
.filter(n -> n % 2 == 0)
.toArray(Integer[]::new);
[Link]("Array: " + [Link](array));
// 5. reduce() - Reduce to single value
// Sum using reduce
Optional<Integer> sum = [Link]()
.reduce((a, b) -> a + b);
[Link]("\nSum: " + [Link](0));
// With identity value
Integer sum2 = [Link]()
.reduce(0, (a, b) -> a + b);
[Link]("Sum with identity: " + sum2);
// Product
Integer product = [Link]()
.reduce(1, (a, b) -> a * b);
[Link]("Product: " + product);
// Find max using reduce
Optional<Integer> max2 = [Link]()
.reduce((a, b) -> a > b ? a : b);
// String concatenation
String concatenated = [Link]("a", "b", "c", "d")
.reduce("", (a, b) -> a + b);
[Link]("Concatenated: " + concatenated);
// Complex reduce
double totalSalary = [Link]()
.map(Employee::getSalary)
.reduce(0.0, Double::sum);
[Link]("Total salary: " + totalSalary);
// 6. Matching operations
// anyMatch - at least one matches
boolean hasEven = [Link]()
.anyMatch(n -> n % 2 == 0);
[Link]("\nHas even? " + hasEven);
// allMatch - all elements match
boolean allPositive = [Link]()
.allMatch(n -> n > 0);
[Link]("All positive? " + allPositive);
// noneMatch - no elements match
boolean noneNegative = [Link]()
.noneMatch(n -> n < 0);
[Link]("None negative? " + noneNegative);
// 7. Finding operations
// findFirst - returns first element
Optional<Integer> first = [Link]()
.filter(n -> n > 5)
.findFirst();
[Link]("\nFirst > 5: " + [Link](0));
// findAny - returns any element (useful for parallel)
Optional<Integer> any = [Link]()
.filter(n -> n > 5)
.findAny();
[Link]("Any > 5: " + [Link](0));
// 8. Short-circuiting operations
// These stop processing as soon as result is determined
boolean result = [Link](1, n -> n + 1)
.limit(1000000)
.anyMatch(n -> n > 100); // Stops at 101
// findFirst also short-circuits
Optional<Integer> firstEven = [Link](1, n -> n + 1)
.filter(n -> n % 2 == 0)
.findFirst(); // Stops at 2
}
}
class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
[Link] = name;
[Link] = salary;
}
public String getName() { return name; }
public double getSalary() { return salary; }
}
8. Collectors - The Power Tools
Collectors are used with the collect() terminal operation to transform stream elements into different forms like
Lists, Sets, Maps, or custom data structures.

import [Link].*;
import [Link].*;
public class CollectorsBasic {
public static void main(String[] args) {
List<String> names = [Link](
"Alice", "Bob", "Charlie", "David", "Eve",
"Frank", "Alice", "Bob"
);
// 1. toList() - Collect to List
List<String> list = [Link]()
.filter(s -> [Link]() > 3)
.collect([Link]());
[Link]("List: " + list);
// 2. toSet() - Collect to Set (removes duplicates)
Set<String> set = [Link]()
.collect([Link]());
[Link]("Set: " + set);
// 3. toCollection() - Collect to specific collection
TreeSet<String> treeSet = [Link]()
.collect([Link](TreeSet::new));
[Link]("TreeSet: " + treeSet);
LinkedList<String> linkedList = [Link]()
.collect([Link](LinkedList::new));
// 4. toMap() - Collect to Map
List<Employee> employees = [Link](
new Employee("Alice", 75000),
new Employee("Bob", 65000),
new Employee("Charlie", 85000)
);
// Simple map (name -> salary)
Map<String, Double> salaryMap = [Link]()
.collect([Link](
Employee::getName,
Employee::getSalary
));
[Link]("\nSalary map: " + salaryMap);
// Handle duplicate keys
List<String> dupes = [Link]("a", "b", "a", "c", "b");
Map<String, Integer> lengthMap = [Link]()
.collect([Link](
s -> s,
String::length,
(existing, replacement) -> existing // Keep first
));
// Collect to specific Map type
TreeMap<String, Double> treeMap = [Link]()
.collect([Link](
Employee::getName,
Employee::getSalary,
(e1, e2) -> e1,
TreeMap::new
));
// 5. joining() - Join strings
String joined = [Link]()
.collect([Link]());
[Link]("\nJoined: " + joined);
// With delimiter
String joinedWithComma = [Link]()
.collect([Link](", "));
[Link]("With comma: " + joinedWithComma);
// With prefix and suffix
String formatted = [Link]()
.distinct()
.collect([Link](", ", "[", "]"));
[Link]("Formatted: " + formatted);
// 6. counting() - Count elements
long count = [Link]()
.collect([Link]());
[Link]("\nCount: " + count);
// 7. summingInt/Long/Double - Sum values
List<Integer> numbers = [Link](1, 2, 3, 4, 5);
int sum = [Link]()
.collect([Link](Integer::intValue));
[Link]("Sum: " + sum);
double totalSalary = [Link]()
.collect([Link](Employee::getSalary));
[Link]("Total salary: " + totalSalary);
// 8. averagingInt/Long/Double - Average
double avgSalary = [Link]()
.collect([Link](Employee::getSalary));
[Link]("Average salary: " + avgSalary);
// 9. summarizingInt/Long/Double - Statistics
IntSummaryStatistics stats = [Link]()
.collect([Link](Integer::intValue));
[Link]("\nStatistics:");
[Link]("Count: " + [Link]());
[Link]("Sum: " + [Link]());
[Link]("Min: " + [Link]());
[Link]("Max: " + [Link]());
[Link]("Average: " + [Link]());
DoubleSummaryStatistics salaryStats = [Link]()
.collect([Link](Employee::getSalary));
[Link]("\nSalary stats: " + salaryStats);
}
}
class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
[Link] = name;
[Link] = salary;
}
public String getName() { return name; }
public double getSalary() { return salary; }
}
8.1 Advanced Collectors
import [Link].*;
import [Link].*;
import [Link].*;
public class CollectorsAdvanced {
public static void main(String[] args) {
List<Employee> employees = [Link](
new Employee("Alice", "IT", 75000, 28),
new Employee("Bob", "HR", 65000, 35),
new Employee("Charlie", "IT", 85000, 32),
new Employee("David", "Finance", 90000, 40),
new Employee("Eve", "IT", 70000, 26),
new Employee("Frank", "HR", 60000, 30)
);
// 1. groupingBy() - Group elements
// Simple grouping by department
Map<String, List<Employee>> byDept = [Link]()
.collect([Link](Employee::getDepartment));
[Link]("By Department:");
[Link]((dept, emps) -> {
[Link](dept + ": " + [Link]() + " employees");
});
// Group and count
Map<String, Long> countByDept = [Link]()
.collect([Link](
Employee::getDepartment,
[Link]()
));
[Link]("\nCount by dept: " + countByDept);
// Group and sum salaries
Map<String, Double> salaryByDept = [Link]()
.collect([Link](
Employee::getDepartment,
[Link](Employee::getSalary)
));
[Link]("Salary by dept: " + salaryByDept);
// Group and get average salary
Map<String, Double> avgSalaryByDept = [Link]()
.collect([Link](
Employee::getDepartment,
[Link](Employee::getSalary)
));
[Link]("Avg salary by dept: " + avgSalaryByDept);
// Group and collect names
Map<String, List<String>> namesByDept = [Link]()
.collect([Link](
Employee::getDepartment,
[Link](
Employee::getName,
[Link]()
)
));
[Link]("\nNames by dept: " + namesByDept);
// Multi-level grouping
Map<String, Map<String, List<Employee>>> byDeptAndAgeGroup =
[Link]()
.collect([Link](
Employee::getDepartment,
[Link](e ->
[Link]() < 30 ? "Young" : "Senior"
)
));
[Link]("\nMulti-level grouping:");
[Link]((dept, ageGroups) -> {
[Link](dept + ":");
[Link]((age, emps) ->
[Link](" " + age + ": " + [Link]()));
});
// 2. partitioningBy() - Split into true/false
Map<Boolean, List<Employee>> partitioned = [Link]()
.collect([Link](
e -> [Link]() > 70000
));
[Link]("\nHigh earners: " +
[Link](true).size());
[Link]("Others: " +
[Link](false).size());
// Partition and count
Map<Boolean, Long> partitionedCount = [Link]()
.collect([Link](
e -> [Link]() > 30,
[Link]()
));
// 3. maxBy() and minBy()
Optional<Employee> highestPaid = [Link]()
.collect([Link](
[Link](Employee::getSalary)
));
[Link](e ->
[Link]("\nHighest paid: " + [Link]()));
// Max salary by department
Map<String, Optional<Employee>> maxByDept = [Link]()
.collect([Link](
Employee::getDepartment,
[Link](
[Link](Employee::getSalary)
)
));
// 4. collectingAndThen() - Transform result
List<String> unmodifiableList = [Link]()
.map(Employee::getName)
.collect([Link](
[Link](),
Collections::unmodifiableList
));
// Get average and round
Double avgSalaryRounded = [Link]()
.collect([Link](
[Link](Employee::getSalary),
Math::round
));
// 5. mapping() - Map before collecting
Set<String> departments = [Link]()
.collect([Link](
Employee::getDepartment,
[Link]()
));
// Get top 2 names per department
Map<String, List<String>> top2ByDept = [Link]()
.collect([Link](
Employee::getDepartment,
[Link](
[Link](),
list -> [Link]()
.sorted([Link](
Employee::getSalary).reversed())
.limit(2)
.map(Employee::getName)
.collect([Link]())
)
));
[Link]("\nTop 2 by dept: " + top2ByDept);
// 6. filtering() - Filter before collecting (Java 9+)
Map<String, Long> seniorCountByDept = [Link]()
.collect([Link](
Employee::getDepartment,
[Link](
e -> [Link]() > 30,
[Link]()
)
));
// 7. flatMapping() - FlatMap before collecting (Java 9+)
List<Department> departments2 = [Link](
new Department("IT", [Link]("Alice", "Bob")),
new Department("HR", [Link]("Charlie", "David"))
);
Map<String, Set<String>> employeesByDept = [Link]()
.collect([Link](
Department::getName,
dept -> new HashSet<>([Link]())
));
// 8. teeing() - Combine two collectors (Java 12+)
// Get both min and max salary
record MinMax(double min, double max) {}
MinMax salaryRange = [Link]()
.collect([Link](
[Link](
[Link](Employee::getSalary)),
[Link](
[Link](Employee::getSalary)),
(min, max) -> new MinMax(
[Link](Employee::getSalary).orElse(0.0),
[Link](Employee::getSalary).orElse(0.0)
)
));
[Link]("\nSalary range: " + salaryRange);
}
}
class Employee {
private String name;
private String department;
private double salary;
private int age;
public Employee(String name, String dept, double salary, int age) {
[Link] = name;
[Link] = dept;
[Link] = salary;
[Link] = age;
}
public String getName() { return name; }
public String getDepartment() { return department; }
public double getSalary() { return salary; }
public int getAge() { return age; }
}
class Department {
private String name;
private List<String> employees;
public Department(String name, List<String> employees) {
[Link] = name;
[Link] = employees;
}
public String getName() { return name; }
public List<String> getEmployees() { return employees; }
}
9. Advanced Stream Patterns
import [Link].*;
import [Link].*;
import [Link].*;
public class AdvancedStreamPatterns {
public static void main(String[] args) {
// 1. Custom Collector
List<String> names = [Link]("Alice", "Bob", "Charlie");
// Collect to comma-separated string (custom way)
String result = [Link]()
.collect(
StringBuilder::new, // Supplier
(sb, s) -> [Link](s).append(", "), // Accumulator
StringBuilder::append // Combiner
).toString();
[Link]("Custom collect: " + result);
// 2. Stream of Streams - flatten
Stream<Stream<Integer>> streamOfStreams = [Link](
[Link](1, 2, 3),
[Link](4, 5, 6),
[Link](7, 8, 9)
);
List<Integer> flattened = streamOfStreams
.flatMap([Link]())
.collect([Link]());
[Link]("Flattened: " + flattened);
// 3. Infinite Stream with limit
List<Integer> fibonacci = [Link](
new int[]{0, 1},
f -> new int[]{f[1], f[0] + f[1]}
)
.limit(10)
.map(f -> f[0])
.collect([Link]());
[Link]("Fibonacci: " + fibonacci);
// 4. Stateful operations (careful!)
List<Integer> numbers = [Link](1, 2, 3, 4, 5);
List<Integer> sums = new ArrayList<>();
// BAD: Modifying external state
[Link]()
.forEach(n -> [Link](n)); // Side effect!
// GOOD: Use collectors
List<Integer> betterSums = [Link]()
.collect([Link]());
// 5. Running sum using reduce with state
List<Integer> runningSum = [Link](1, 5)
.boxed()
.collect(() -> new ArrayList<Integer>() {{add(0);}},
(list, n) -> [Link]([Link]([Link]()-1) + n),
ArrayList::addAll);
[Link]("Running sum: " + runningSum);
// 6. Conditional operations
List<String> items = [Link]("a", "b", "c", "d");
boolean addSuffix = true;
List<String> processed = [Link]()
.map(s -> addSuffix ? s + "_suffix" : s)
.collect([Link]());
// 7. Peek for debugging
List<Integer> debugged = [Link]()
.peek(n -> [Link]("Original: " + n))
.map(n -> n * 2)
.peek(n -> [Link]("Doubled: " + n))
.filter(n -> n > 5)
.peek(n -> [Link]("Filtered: " + n))
.collect([Link]());
// 8. Converting between stream types
// Stream to IntStream
IntStream intStream = [Link]("1", "2", "3")
.mapToInt(Integer::parseInt);
// IntStream to Stream
Stream<Integer> boxedStream = [Link](1, 2, 3)
.boxed();
// 9. Cartesian Product
List<String> colors = [Link]("Red", "Green");
List<String> sizes = [Link]("S", "M", "L");
List<String> products = [Link]()
.flatMap(color ->
[Link]().map(size -> color + "-" + size))
.collect([Link]());
[Link]("Products: " + products);
// 10. Windowing/Batching
List<Integer> largeList = [Link](1, 10)
.boxed()
.collect([Link]());
int batchSize = 3;
List<List<Integer>> batches = [Link](0,
([Link]() + batchSize - 1) / batchSize)
.mapToObj(i -> [Link](
i * batchSize,
[Link]((i + 1) * batchSize, [Link]())
))
.collect([Link]());
[Link]("Batches: " + batches);
// 11. Removing duplicates while maintaining order
List<Integer> withDupes = [Link](1, 2, 2, 3, 3, 4, 1);
List<Integer> unique = [Link]()
.distinct()
.collect([Link]());
// 12. Top N elements
List<Integer> top3 = [Link]()
.sorted([Link]())
.limit(3)
.collect([Link]());
// 13. Frequency map
List<String> words = [Link]("a", "b", "a", "c", "b", "a");
Map<String, Long> frequency = [Link]()
.collect([Link](
[Link](),
[Link]()
));
[Link]("Frequency: " + frequency);
// 14. Index with element
List<String> indexed = [Link](0, [Link]())
.mapToObj(i -> i + ": " + [Link](i))
.collect([Link]());
[Link]("Indexed: " + indexed);
// 15. Reverse a list using streams
List<Integer> reversed = [Link](1, [Link]())
.map(i -> [Link]([Link]() - i))
.boxed()
.collect([Link]());
}
}
10. Parallel Streams and Performance
import [Link].*;
import [Link].*;
import [Link].*;
public class ParallelStreams {
public static void main(String[] args) {
List<Integer> numbers = [Link](1, 1000000)
.boxed()
.collect([Link]());
// 1. Creating parallel streams
// From collection
Stream<Integer> parallelStream1 = [Link]();
// From sequential stream
Stream<Integer> parallelStream2 = [Link]().parallel();
// Check if parallel
boolean isParallel = [Link]();
[Link]("Is parallel? " + isParallel);
// 2. Sequential vs Parallel performance
// Sequential
long start = [Link]();
long seqSum = [Link]()
.mapToLong(Integer::longValue)
.sum();
long seqTime = [Link]() - start;
// Parallel
start = [Link]();
long parSum = [Link]()
.mapToLong(Integer::longValue)
.sum();
long parTime = [Link]() - start;
[Link]("Sequential: " + seqTime + "ms");
[Link]("Parallel: " + parTime + "ms");
[Link]("Speedup: " + (double)seqTime / parTime + "x");
// 3. When to use parallel streams
// GOOD: CPU-intensive operations on large datasets
List<String> largeList = new ArrayList<>();
for(int i = 0; i < 100000; i++) {
[Link]("Item" + i);
}
long count = [Link]()
.filter(s -> [Link]("5"))
.map(String::toUpperCase)
.distinct()
.count();
// BAD: Small datasets (overhead > benefit)
List<Integer> small = [Link](1, 2, 3, 4, 5);
// Don't use parallel for this!
// 4. Thread safety issues
// WRONG: Not thread-safe
List<Integer> resultList = new ArrayList<>();
[Link]()
.forEach(n -> [Link](n)); // Race condition!
// RIGHT: Use collect
List<Integer> safeList = [Link]()
.collect([Link]());
// RIGHT: Use concurrent collection
List<Integer> concurrentList = new CopyOnWriteArrayList<>();
[Link]()
.forEach(concurrentList::add);
// 5. Order preservation
List<Integer> ordered = [Link](1, 2, 3, 4, 5);
// forEach - may print in any order
[Link]("\nParallel forEach:");
[Link]()
.forEach(n -> [Link](n + " "));
// forEachOrdered - maintains order
[Link]("\nParallel forEachOrdered:");
[Link]()
.forEachOrdered(n -> [Link](n + " "));
[Link]();
// 6. Controlling parallelism
// Set parallelism level
[Link]("[Link]", "4");
// Using custom ForkJoinPool
ForkJoinPool customPool = new ForkJoinPool(4);
try {
long customSum = [Link](() ->
[Link]()
.mapToLong(Integer::longValue)
.sum()
).get();
[Link]("Custom pool sum: " + customSum);
} catch(Exception e) {
[Link]();
} finally {
[Link]();
}
// 7. Stateful operations - problematic in parallel
// BAD: Stateful lambda
List<Integer> badResult = new ArrayList<>();
[Link]()
.parallel()
.map(n -> {
[Link](n); // Modifies external state!
return n * 2;
})
.collect([Link]());
// GOOD: Stateless
List<Integer> goodResult = [Link]()
.parallel()
.map(n -> n * 2) // Pure function
.collect([Link]());
// 8. reduce() with parallel streams
// Must be associative for correct parallel execution
int sum1 = [Link]()
.reduce(0, Integer::sum); // Associative: OK
// Non-associative operation
int badSubtract = [Link]()
.reduce(0, (a, b) -> a - b); // NOT associative: WRONG!
// 9. When NOT to use parallel streams
// Small datasets (< 1000 elements)
// I/O operations (network, database, file)
// Stateful operations
// Order-dependent operations
// Simple operations (overhead > benefit)
// 10. Benchmarking
[Link]("\nBenchmarking different operations:");
// Operation 1: Simple map
benchmarkOperation("Simple map",
() -> [Link]().map(n -> n * 2).count(),
() -> [Link]().map(n -> n * 2).count()
);
// Operation 2: Complex filter
benchmarkOperation("Complex filter",
() -> [Link]()
.filter(n -> isPrime(n))
.count(),
() -> [Link]()
.filter(n -> isPrime(n))
.count()
);
}
static void benchmarkOperation(String name,
Runnable sequential,
Runnable parallel) {
long start = [Link]();
[Link]();
long seqTime = [Link]() - start;
start = [Link]();
[Link]();
long parTime = [Link]() - start;
[Link](name + ":");
[Link](" Sequential: " + seqTime/1_000_000 + "ms");
[Link](" Parallel: " + parTime/1_000_000 + "ms");
[Link](" Speedup: " +
[Link]("%.2f", (double)seqTime/parTime) + "x");
}
static boolean isPrime(int n) {
if(n < 2) return false;
for(int i = 2; i <= [Link](n); i++) {
if(n % i == 0) return false;
}
return true;
}
}
// Guidelines:
// 1. Use parallel for CPU-intensive, large datasets
// 2. Avoid for I/O operations
// 3. Ensure thread safety
// 4. Test and benchmark
// 5. Prefer stateless operations
11. Optional Class - Null Safety
import [Link].*;
import [Link].*;
public class OptionalExamples {
public static void main(String[] args) {
// 1. Creating Optional
// of() - throws NPE if null
Optional<String> opt1 = [Link]("Hello");
// Optional<String> opt2 = [Link](null); // NPE!
// ofNullable() - handles null
Optional<String> opt3 = [Link]("World");
Optional<String> opt4 = [Link](null);
// empty()
Optional<String> opt5 = [Link]();
// 2. Checking presence
if([Link]()) {
[Link]("Value: " + [Link]());
}
// Java 11+
if([Link]()) {
[Link]("No value");
}
// 3. Getting values
// get() - throws NoSuchElementException if empty
String value1 = [Link](); // OK
// String value2 = [Link](); // Exception!
// orElse() - provide default
String value3 = [Link]("Default");
[Link]("Value3: " + value3);
// orElseGet() - provide supplier (lazy)
String value4 = [Link](() -> "Computed Default");
// orElseThrow() - throw exception
String value5 = [Link]();
String value6 = [Link](
() -> new IllegalStateException("No value!")
);
// 4. Transforming Optional
Optional<String> name = [Link]("john");
// map() - transform if present
Optional<String> upper = [Link](String::toUpperCase);
[Link]("Upper: " + [Link]());
Optional<Integer> length = [Link](String::length);
[Link]("Length: " + [Link]());
// Chaining maps
Optional<Character> firstChar = name
.map(String::toUpperCase)
.map(s -> [Link](0));
// flatMap() - for nested Optionals
Optional<Optional<String>> nested = [Link](
[Link]("Nested")
);
// Wrong: results in Optional<Optional<String>>
// Optional<Optional<String>> wrong = [Link](opt -> opt);
// Right: flatMap flattens
Optional<String> flattened = [Link](opt -> opt);
// Real example
Optional<String> result = findUser("john")
.flatMap(user -> findAddress(user))
.flatMap(addr -> findCity(addr));
// 5. Filtering Optional
Optional<Integer> number = [Link](42);
Optional<Integer> even = [Link](n -> n % 2 == 0);
[Link]("Even: " + [Link]());
Optional<Integer> negative = [Link](n -> n < 0);
[Link]("Negative: " + [Link]());
// 6. ifPresent() - consume if present
[Link](s -> [Link]("Value: " + s));
[Link]([Link]::println);
// ifPresentOrElse() - Java 9+
[Link](
s -> [Link]("Found: " + s),
() -> [Link]("Not found")
);
// 7. or() - Alternative Optional (Java 9+)
Optional<String> primary = [Link]();
Optional<String> secondary = [Link]("Secondary");
Optional<String> chosen = [Link](() -> secondary);
[Link]("Chosen: " + [Link]());
// 8. stream() - Convert to Stream (Java 9+)
List<Optional<String>> optionals = [Link](
[Link]("A"),
[Link](),
[Link]("B"),
[Link](),
[Link]("C")
);
List<String> values = [Link]()
.flatMap(Optional::stream) // Filters out empties
.collect([Link]());
[Link]("Values: " + values);
// 9. Common patterns
// Pattern 1: Avoid get()
// BAD
Optional<String> opt = getOptionalValue();
if([Link]()) {
String val = [Link](); // Redundant check
processValue(val);
}
// GOOD
[Link](val -> processValue(val));
// Pattern 2: Chaining operations
String processed = getOptionalValue()
.map(String::trim)
.map(String::toUpperCase)
.filter(s -> [Link]() > 3)
.orElse("DEFAULT");
// Pattern 3: Avoiding null checks
// OLD WAY
User user = findUserOldWay("john");
if(user != null) {
Address addr = [Link]();
if(addr != null) {
String city = [Link]();
if(city != null) {
[Link](city);
}
}
}
// NEW WAY
findUser("john")
.flatMap(u -> [Link]())
.flatMap(a -> [Link]())
.ifPresent([Link]::println);
// 10. Primitive Optionals
OptionalInt optInt = [Link](42);
OptionalLong optLong = [Link](100L);
OptionalDouble optDouble = [Link](3.14);
int value = [Link](0);
// 11. Optional in streams
List<String> names = [Link]("Alice", "Bob", null, "Charlie");
List<String> filtered = [Link]()
.map(s -> [Link](s))
.filter(Optional::isPresent)
.map(Optional::get)
.collect([Link]());
// Better with flatMap
List<String> better = [Link]()
.flatMap(s -> [Link](s).stream())
.collect([Link]());
// Find first even number
Optional<Integer> firstEven = [Link](1, 3, 5, 6, 8)
.stream()
.filter(n -> n % 2 == 0)
.findFirst();
[Link](n ->
[Link]("First even: " + n));
}
static Optional<String> getOptionalValue() {
return [Link]("test");
}
static void processValue(String s) {
[Link]("Processing: " + s);
}
static Optional<User> findUser(String name) {
return [Link](new User(name));
}
static User findUserOldWay(String name) {
return new User(name);
}
static Optional<Address> findAddress(User user) {
return [Link](new Address());
}
static Optional<String> findCity(Address addr) {
return [Link]("New York");
}
}
class User {
private String name;
public User(String name) { [Link] = name; }
public Optional<Address> getAddress() {
return [Link](new Address());
}
}
class Address {
public Optional<String> getCity() {
return [Link]("New York");
}
}
// Best Practices:
// 1. Never assign null to Optional
// 2. Don't use Optional as field
// 3. Use for return types, not parameters
// 4. Don't use get() without isPresent()
// 5. Prefer orElse, map, flatMap over get()
12. Real-world Interview Problems
import [Link].*;
import [Link].*;
import [Link].*;
public class InterviewProblems {
// Q1: Find duplicate elements in a list
public static List<Integer> findDuplicates(List<Integer> list) {
return [Link]()
.collect([Link](
[Link](),
[Link]()
))
.entrySet().stream()
.filter(e -> [Link]() > 1)
.map([Link]::getKey)
.collect([Link]());
// Alternative
Set<Integer> seen = new HashSet<>();
return [Link]()
.filter(n -> ![Link](n))
.distinct()
.collect([Link]());
}
// Q2: Second highest number
public static Optional<Integer> secondHighest(List<Integer> list) {
return [Link]()
.distinct()
.sorted([Link]())
.skip(1)
.findFirst();
}
// Q3: Find employees with nth highest salary
public static List<Employee> nthHighestSalary(
List<Employee> employees, int n) {
return [Link]()
.sorted([Link](
Employee::getSalary).reversed())
.skip(n - 1)
.limit(1)
.collect([Link]());
// Or get the salary value
Optional<Double> nthSalary = [Link]()
.map(Employee::getSalary)
.distinct()
.sorted([Link]())
.skip(n - 1)
.findFirst();
}
// Q4: Group anagrams together
public static Map<String, List<String>> groupAnagrams(
List<String> words) {
return [Link]()
.collect([Link](word -> {
char[] chars = [Link]();
[Link](chars);
return new String(chars);
}));
}
// Q5: Remove duplicates while preserving order
public static List<Integer> removeDuplicates(List<Integer> list) {
return [Link]()
.distinct()
.collect([Link]());
// Using LinkedHashSet
return new ArrayList<>(new LinkedHashSet<>(list));
}
// Q6: Find first non-repeated character
public static Optional<Character> firstNonRepeated(String str) {
return [Link]()
.mapToObj(c -> (char) c)
.collect([Link](
[Link](),
LinkedHashMap::new,
[Link]()
))
.entrySet().stream()
.filter(e -> [Link]() == 1)
.map([Link]::getKey)
.findFirst();
}
// Q7: Partition list into even and odd
public static Map<Boolean, List<Integer>> partitionEvenOdd(
List<Integer> list) {
return [Link]()
.collect([Link](n -> n % 2 == 0));
}
// Q8: Find common elements between two lists
public static List<Integer> findCommon(
List<Integer> list1, List<Integer> list2) {
return [Link]()
.filter(list2::contains)
.distinct()
.collect([Link]());
// More efficient with Set
Set<Integer> set2 = new HashSet<>(list2);
return [Link]()
.filter(set2::contains)
.distinct()
.collect([Link]());
}
// Q9: Calculate sum of squares of even numbers
public static int sumOfSquaresOfEvens(List<Integer> list) {
return [Link]()
.filter(n -> n % 2 == 0)
.mapToInt(n -> n * n)
.sum();
}
// Q10: Flatten nested list
public static List<Integer> flattenList(
List<List<Integer>> nested) {
return [Link]()
.flatMap(List::stream)
.collect([Link]());
}
// Q11: Top K frequent elements
public static List<Integer> topKFrequent(
List<Integer> list, int k) {
return [Link]()
.collect([Link](
[Link](),
[Link]()
))
.entrySet().stream()
.sorted([Link].<Integer, Long>comparingByValue()
.reversed())
.limit(k)
.map([Link]::getKey)
.collect([Link]());
}
// Q12: Reverse each word in a sentence
public static String reverseWords(String sentence) {
return [Link]([Link](" "))
.map(word -> new StringBuilder(word).reverse())
.collect([Link](" "));
}
// Q13: Find pairs that sum to target
public static List<int[]> findPairs(List<Integer> list, int target) {
Set<Integer> seen = new HashSet<>();
return [Link]()
.filter(n -> {
int complement = target - n;
if([Link](complement)) {
return true;
}
[Link](n);
return false;
})
.map(n -> new int[]{target - n, n})
.collect([Link]());
}
// Q14: Merge and sort multiple sorted lists
public static List<Integer> mergeSortedLists(
List<List<Integer>> lists) {
return [Link]()
.flatMap(List::stream)
.sorted()
.collect([Link]());
}
// Q15: Calculate average salary by department
public static Map<String, Double> avgSalaryByDept(
List<Employee> employees) {
return [Link]()
.collect([Link](
Employee::getDepartment,
[Link](Employee::getSalary)
));
}
// Q16: Find longest string
public static Optional<String> longestString(List<String> list) {
return [Link]()
.max([Link](String::length));
}
// Q17: Convert list to map (name -> employee)
public static Map<String, Employee> listToMap(
List<Employee> employees) {
return [Link]()
.collect([Link](
Employee::getName,
[Link](),
(e1, e2) -> e1 // Handle duplicates
));
}
// Q18: Check if all elements are positive
public static boolean allPositive(List<Integer> list) {
return [Link]()
.allMatch(n -> n > 0);
}
// Q19: Find employees older than 30 in IT dept
public static List<Employee> findITSeniors(
List<Employee> employees) {
return [Link]()
.filter(e -> [Link]().equals("IT"))
.filter(e -> [Link]() > 30)
.collect([Link]());
}
// Q20: Implement map() using reduce()
public static <T, R> List<R> customMap(
List<T> list, Function<T, R> mapper) {
return [Link]()
.reduce(
new ArrayList<R>(),
(acc, item) -> {
List<R> newList = new ArrayList<>(acc);
[Link]([Link](item));
return newList;
},
(list1, list2) -> {
List<R> combined = new ArrayList<>(list1);
[Link](list2);
return combined;
}
);
}
public static void main(String[] args) {
// Test Q1
List<Integer> numbers = [Link](1, 2, 3, 2, 4, 3, 5);
[Link]("Duplicates: " + findDuplicates(numbers));
// Test Q2
[Link]("Second highest: " +
secondHighest([Link](5, 3, 8, 1, 9, 2)));
// Test Q6
[Link]("First non-repeated: " +
firstNonRepeated("swiss"));
// Test Q12
[Link]("Reversed words: " +
reverseWords("Hello World Java"));
}
}
class Employee {
private String name;
private String department;
private double salary;
private int age;
public Employee(String name, String dept, double salary, int age) {
[Link] = name;
[Link] = dept;
[Link] = salary;
[Link] = age;
}
public String getName() { return name; }
public String getDepartment() { return department; }
public double getSalary() { return salary; }
public int getAge() { return age; }
}
13. Common Pitfalls and Best Practices
Common Pitfalls to Avoid:
1. REUSING STREAMS
// WRONG
Stream<String> stream = [Link]();
[Link]([Link]::println);
[Link]([Link]::println); // IllegalStateException!
// RIGHT
[Link]().forEach([Link]::println);
[Link]().forEach([Link]::println);
2. MODIFYING SOURCE WHILE STREAMING
// WRONG
[Link]().forEach(item -> [Link](item)); // ConcurrentModificationException!
// RIGHT
list = [Link]()
.filter(item -> someCondition)
.collect([Link]());
3. SIDE EFFECTS IN LAMBDAS
// WRONG
List<Integer> results = new ArrayList<>();
[Link]()
.forEach(n -> [Link](n * 2)); // Side effect!
// RIGHT
List<Integer> results = [Link]()
.map(n -> n * 2)
.collect([Link]());
4. USING peek() FOR NON-DEBUGGING
// WRONG
[Link](list::add) // Don't use peek for actions
.collect([Link]());
// RIGHT
[Link](list::add);
5. NOT HANDLING EMPTY OPTIONALS
// WRONG
Optional<String> opt = findValue();
String value = [Link](); // NoSuchElementException if empty!
// RIGHT
String value = [Link]("default");
6. USING PARALLEL WITHOUT TESTING
// WRONG
[Link]() // May be slower!
.map(simpleOperation)
.collect([Link]());
// RIGHT - Test first!
// Use parallel only for CPU-intensive ops on large data
7. COLLECTING TO WRONG TYPE
// INEFFICIENT
Set<String> set = [Link]()
.collect([Link]()) // Creates list first
.stream()
.collect([Link]()); // Then converts to set
// EFFICIENT
Set<String> set = [Link]()
.collect([Link]());
8. EXCESSIVE BOXING/UNBOXING
// WRONG
Stream<Integer> stream = [Link](1, 1000)
.boxed(); // Boxing overhead
// RIGHT
IntStream stream = [Link](1, 1000);
int sum = [Link]();
9. NOT USING METHOD REFERENCES
// LESS READABLE
[Link]()
.map(s -> [Link]())
.forEach(s -> [Link](s));
// MORE READABLE
[Link]()
.map(String::toUpperCase)
.forEach([Link]::println);
10. IGNORING NULL VALUES
// WRONG
[Link]()
.map(String::length) // NPE if list contains null!
.collect([Link]());
// RIGHT
[Link]()
.filter(Objects::nonNull)
.map(String::length)
.collect([Link]());
Best Practices:
1. PREFER METHOD REFERENCES
✓ Use String::toUpperCase over s -> [Link]()
2. USE PRIMITIVE STREAMS
✓ IntStream, LongStream, DoubleStream for primitives
3. CHOOSE RIGHT COLLECTOR
✓ toList() for most cases
✓ toSet() for unique elements
✓ toMap() for key-value pairs
✓ groupingBy() for grouping
✓ partitioningBy() for binary split
4. LIMIT STREAM SIZE
✓ Use limit() with infinite streams
✓ Use takeWhile() for conditional limits
5. USE PARALLEL WISELY
✓ Only for large datasets (>1000 elements)
✓ Only for CPU-intensive operations
✓ Avoid for I/O operations
✓ Test performance before using
6. MAKE OPERATIONS STATELESS
✓ Don't modify external state
✓ Don't depend on external mutable state
7. USE OPTIONAL PROPERLY
✓ Return Optional, don't take as parameter
✓ Don't use [Link]() without checking
✓ Prefer orElse, map, flatMap
8. KEEP PIPELINES SHORT
✓ Too many operations? Split into methods
✓ Name complex predicates/functions
9. USE [Link] FOR MULTIPLE STATS
✓ Get min and max in one pass
10. DOCUMENT COMPLEX STREAMS
✓ Add comments for non-obvious operations
Performance Tips:
1. Avoid unnecessary boxing/unboxing
2. Use parallel for CPU-intensive, large datasets
3. Filter before expensive operations (map, flatMap)
4. Use primitive streams when possible
5. Prefer collect() over reduce() for mutable containers
6. Use short-circuiting operations (findFirst, anyMatch)
7. Avoid creating intermediate collections
8. Consider memory usage for large datasets
9. Use appropriate data structures
10. Profile before optimizing!
Summary and Key Takeaways
Streams and Functional Programming - Quick Reference:
LAMBDA SYNTAX:
• () -> expression
• param -> expression
• (param1, param2) -> expression
• (params) -> { statements; return value; }
FUNCTIONAL INTERFACES:
• Predicate<T>: T -> boolean
• Function<T,R>: T -> R
• Consumer<T>: T -> void
• Supplier<T>: () -> T
• UnaryOperator<T>: T -> T
• BinaryOperator<T>: (T,T) -> T
METHOD REFERENCES:
• Static: ClassName::staticMethod
• Instance (specific): object::instanceMethod
• Instance (arbitrary): ClassName::instanceMethod
• Constructor: ClassName::new
INTERMEDIATE OPERATIONS:
• filter(Predicate): Keep matching elements
• map(Function): Transform elements
• flatMap(Function): Flatten nested structures
• distinct(): Remove duplicates
• sorted(): Sort elements
• peek(Consumer): Debug/inspect
• limit(n): Take first n
• skip(n): Skip first n
TERMINAL OPERATIONS:
• forEach(Consumer): Perform action
• count(): Count elements
• collect(Collector): Collect to collection
• reduce(BinaryOp): Reduce to single value
• min/max(Comparator): Find min/max
• anyMatch/allMatch/noneMatch: Boolean tests
• findFirst/findAny: Find element
COLLECTORS:
• toList(), toSet(), toMap()
• joining(delimiter)
• groupingBy(classifier)
• partitioningBy(predicate)
• counting(), summingInt(), averagingDouble()
• maxBy(), minBy()
• collectingAndThen()
PARALLEL STREAMS:
✓ Use for large datasets (>1000)
✓ Use for CPU-intensive operations
✗ Avoid for I/O operations
✗ Avoid for small datasets
✗ Ensure thread safety
OPTIONAL:
• of(value): Create (non-null)
• ofNullable(value): Create (nullable)
• empty(): Empty optional
• isPresent(), isEmpty(): Check
• get(): Get value (unsafe)
• orElse(default): Get or default
• map(Function): Transform
• flatMap(Function): Flatten
• filter(Predicate): Filter
INTERVIEW TIPS:
1. Know common operations by heart
2. Practice stream pipelines
3. Understand collectors
4. Master Optional usage
5. Know when to use parallel
6. Practice real-world problems
7. Understand performance implications
8. Know pitfalls and best practices
Remember: Think declaratively, not imperatively!

You might also like