Java Streams Functional Programming
Java Streams Functional Programming
Functional Programming
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.
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.
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.
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 -> )
.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!