Lambdas & Streams
Lambdas — Functions as Values
Before Java 8, if you wanted to pass a piece of behavior to a method, you had to create a whole class. It was like writing a full letter when all you needed was a sticky note. Lambdas are those sticky notes — tiny, anonymous functions you can pass around.
The syntax is simple: (parameters) -> expression
Some examples:
(a, b) -> a + b— takes two values, returns their sum(s) -> s.length()— takes a string, returns its length() -> System.out.println("Hello!")— takes nothing, prints hello
Lambdas work with functional interfaces — interfaces with exactly one abstract method. You can use them with collections like Maps and Sets to write concise data-processing code. Java provides many built-in ones like Predicate (test), Function (transform), Consumer (act on), and Comparator (compare).
Lambda Basics & Functional Interfaces
Streams — Processing Data Like a Pipeline
Think of a stream as a conveyor belt in a factory. Data items go in one end, pass through various stations (filter, transform, sort), and come out the other end as a finished product.
Streams don't change the original data — they create a new result. Understanding how chained operations affect time complexity helps you write efficient pipelines. They're also lazy: nothing actually happens until you call a terminal operation (like collect(), forEach(), or count()).
Common stream operations:
filter()— keep only items that match a conditionmap()— transform each item into something elsesorted()— put items in orderreduce()— combine all items into one resultcollect()— gather results into a list, set, or other collection
Streams in Action
Streams with Strings & Method References
::) are just shorthand for simple lambdas. String::toUpperCase is the same as s -> s.toUpperCase(). Use them when the lambda just calls a single method — it makes your code read like English. System.out::println, Integer::parseInt, Math::max — clean and clear.