1/11
Name | Mastery | Learn | Test | Matching | Spaced |
---|
No study sessions yet.
What is Function programming? Why we need ??
Consider function as First-class Functions: You can treat functions just like any other variable. You can pass them as arguments, return them from other functions, and assign them to variables.
Why?
Pure Functions and Avoiding side effects - goal is to minimize parts of code that change state outside the scope.
Declarative, not imperative: what you want to achieve rather than how you want to achieve.
What is pure function and why we need it??
A function is pure
Its output depends only on its input arguments (no hidden state).
It has no side effects (it doesn't modify any external state, like global variables, or perform I/O like writing to a file).
This makes functions incredibly predictable, easy to test, and perfect for parallel execution.
The goal is to minimize parts of the code that change state outside their own scope. This leads to programs that are easier to reason about.
What is lambda function?? Where it used ?? why it’s there??
Provides clear and concise way to implement functional interface (SAM such as Runnable
, Comparator
, or those in the java.util.function
package (e.g., Predicate
, Function
)
Replacement of verbose anonymous inner class, to reduce boilerplate code.
Used only when functional interface is expected.
are pure functions → output depends only on its input and no side effects (external state change, perform I/O)
what is functional interface??
Enables functional programming in Java.
It’s an interface with SAM.
Single method defines the contract and lambda expression provides the implementation.
May have multiple default (backward compatibility), static, private methods (helper).
What are built-in functional interfaces??
Available in java.util.function package.
Predicate<T> - Test a condition and return boolean - streams.filter() - composition: and(), or(), negate
Function<T,R>: Transform input T to output R - map(), flatmap() - composition: andThen(), compose()
Consumer<T>: Performing actions on elements with side effect - .forEach(), .peek() - composition: andThen()
Supplier<T>: supply a value without input
What does stream do and why it’s there?
Enables declarative and functional approach to processing sequence of data.
Allows computation over data sources by defining sequence of multiple intermediate operations (lazy) and one terminal operations (eager).
The intermediate operations (filter, map, flatmap, sorted) are lambda expression or method references.—which are implemented as pure functions (stateless, no side-effects, non-interfering)
Terminal operations (collect(), forEach(), findFirst) are eager and initiate the pipeline execution.
Sequential and parallel execution are allowed.
Best practice for streams
✅ Use IntStream
, LongStream
, DoubleStream
→ avoid autoboxing
✅ Keep lambdas pure → no side effects
✅ Handle checked exceptions → try–catch or helper method
✅ Long pipelines? → split into smaller/named methods
✅ Parallel streams → only for large data (>10k), test performance first
Where streams API shines
🔄 Data Processing Pipelines
Filter, map, reduce collections
👉 Example: Filter active users, map to emails, collect to list
📊 Aggregations
Sum, average, count, grouping
👉 Example: Total sales per region, average order value
📂 Transformations
Convert structures or formats
👉 Example: Convert list of objects → Map<ID, Name>
⚡ Bulk Operations
Concise operations on large datasets
👉 Example: Apply discount to all products in catalog
🧵 Parallelism
Leverage multi-core for heavy data
👉 Example: Image processing, log file analysis, scientific computation
Stream operations
🎯 Creating Streams
From collections: collection.stream()
, collection.parallelStream()
From values/arrays: Stream.of(...)
, Stream.empty()
Infinite: Stream.generate(supplier)
, Stream.iterate(seed, f)
Ranges: IntStream.range(1, n)
Files: Files.lines(path)
🔍 Intermediate (build pipeline)
Filter: filter(p -> …)
Map: map(f)
, flatMap(f)
, mapToInt/Long/Double
Stateful: distinct()
, sorted()
, limit(n)
, skip(n)
Structural: peek(consumer)
(debug only), takeWhile
, dropWhile
(Java 9)
Order: unordered()
(perf hint)
🏁 Terminal (produce result)
Collection: toList()
(JDK 16+), collect(Collectors.toList())
, toSet()
Reduction: reduce(identity, accumulator)
, sum()
, average()
, max()
, min()
(on primitive streams)
Matching: anyMatch
, allMatch
, noneMatch
Optional: findFirst()
, findAny()
Consumption: forEach(...)
, forEachOrdered(...)
📊 Common Collectors
joining(",")
→ concatenate strings
groupingBy(f)
→ Map<K, List<V>>
partitioningBy(p)
→ {true:…, false:…}
mapping(f, toList())
→ transform + collect
what is optional API and where can we use it?
immutable container (thread safe) that represents either a presence or absence of the value → to reduce NullPointerException.
Usage:
Primary as Return type for methods where result might be absent.
Repository/Data Access Layer methods (e.g., findById(id)
).
Service layer lookups (e.g., getCurrentUser()
, findLatestOrder()
).
Any search or calculation that might not yield a result (e.g., finding a max/min in a potentially empty collection, parsing input).
Stream pipelines where operations like findFirst
or max
might not have a result. The stream terminal operation returns an Optional
to you.
Where we should not use optional ?
⛔ Don’t use Optional
in entity fields (JPA), constructors, or setters.
⛔ Don’t use Optional
as a method parameter.
⚖ Performance: keep Optional
at API boundaries. Inside hot loops, prefer direct null checks or domain-specific defaults.
What are all the important operations and gotchas for optional API ?
Optional Cheat Sheet1. Create It
Optional.of(value)
- Value MUST NOT be null.
Optional.ofNullable(value)
- Value CAN be null (becomes empty).
Optional.empty()
- Make an empty box.
2. Get Value From It (Safely!)
Never just use .get()
alone!
.orElse(defaultValue)
- Get value or use a default.
.orElseGet(() -> getDefault())
- Get value or run code to get a default (lazy).
.orElseThrow()
- Get value or throw NoSuchElementException
.
.orElseThrow(() -> new MyException())
- Get value or throw your custom exception.
3. Check It
.isPresent()
- true
if value exists.
.isEmpty()
(Java 11+) - true
if value is missing.
4. Do Something If Value Exists
.ifPresent(value -> doSomething(value))
- Act on the value only if it's there.
5. Transform It
.map(value -> transformedValue)
- Transform the value inside (e.g., user -> user.getName()
).
.filter(value -> condition)
- Keep the value only if it meets a condition.
Gotchas:
Explain the difference between orElse (always evaluates its argument) and orElseGet (supplier executed only when empty). This catches many candidates.
Don’t call get() unless you just checked presence and there’s no better alternative.