Streams and Lambdas¶
Lambda Expressions¶
Enables functional programming by treating functions as first-class citizens.
- Syntax:
(parameters) -> expression
or(parameters) -> { statements }
Lamba Example
Functional Interfaces¶
A functional interface is an interface with only one abstract method. This is important because lambda expressions can be used to provide the implementation for these interfaces.
Functional Interface Example
@FunctionalInterface // Optional but ensures the interface has only one abstract method.
interface MyFunction {
int apply(int a, int b); // Single abstract method
}
Now, when you want to use this interface, you don’t need to create a class and provide an implementation like before. Instead, you can use a lambda expression to quickly provide the logic.
MyFunction addition = (a, b) -> a + b; // Lambda expression for addition
System.out.println(addition.apply(5, 3)); // Output: 8
Explanation
(a, b) -> a + b
is the lambda expression.- It directly implements the
apply(int a, int b)
method of theMyFunction
interface.
Method References¶
A method reference is a shorthand way of writing a lambda when a method already exists that matches the lambda’s purpose. This makes the code more concise and readable.
Example with forEach
and Method Reference
Consider the following list of names:
You want to print all names using forEach()
. You could do it with a lambda like this:
Now, Java provides a shorthand: Method Reference. Since System.out.println()
already matches the structure (String) -> void
, you can write:
Explanation
System.out::println
is a method reference to theprintln()
method ofSystem.out
.- It behaves just like the lambda
name -> System.out.println(name)
but is cleaner.
Use method references when:
- A lambda calls an existing method directly without modifying the input.
- It improves readability of the code.
More Examples
// 1. Static method reference
Function<String, Integer> parse = Integer::parseInt;
System.out.println(parse.apply("123")); // Output: 123
// 2. Instance method reference on an arbitrary object
List<String> words = Arrays.asList("one", "two", "three");
words.sort(String::compareToIgnoreCase); // Sorts case-insensitively
Streams API¶
Introduced in Java 8 to process collections in a declarative way.
Core Stream Operations
Creation¶
Stream<Integer> stream = Stream.of(1, 2, 3, 4);
List<String> list = Arrays.asList("A", "B", "C");
Stream<String> streamFromList = list.stream();
Intermediate Operations (return new streams, lazy evaluation)¶
// Filters elements based on a predicate.
List<Integer> evenNumbers = stream.filter(n -> n % 2 == 0).toList();
Terminal Operations (trigger computation)¶
// Collects elements into a collection.
List<String> newList = list.stream().filter(s -> s.startsWith("A")).collect(Collectors.toList());
// Reduces the elements to a single result.
int sum = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
Parallel Streams¶
Examples Streams/Lambdas¶
Find the sum of even numbers
Convert List of Strings to Uppercase
Group elements by length
Best Practices¶
- Prefer Streams over loops for readability.
- Use parallel streams with caution (only when operations are stateless).
- Avoid side effects in Intermediate Operations (e.g., printing inside
map()
).