Java Streams and Lambda quick introduction
Here are some common interview questions about Java Streams. This post is not exhaustive. The subject is pretty big. The goal of the document is to be a quick refresher when an overview of the lambda expression is needed. The document will be updated according to the needs and in case of requests.
Which are the components of a Stream?
The Source can be finite (e.g. Collection) or infinite (e.g. mathematical operation).
Stream.of("one", "two", "three", "four") // finite stream
.filter(e -> e.length() > 3)
.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e))
.collect(Collectors.toList());
In this example, which is copied from the java documentation we have an infinite Stream created with .of method provided by the Stream class.
filter
is an intermediate operation that apply the predicate to each element of the stream and returns a stream with the elements that match the predicate.
peek
return the original stream, it applies a function to each element of the stream. It's generally used to debug or log the stream. It's the equivalent of tap
in RxJS.
map
returns a stream with the result the function passed as a parameter.
collect
this is a terminal operation, collect performs a reduction collecting the result in a container. You can find the list of predefined [collectors in the documentation](Collectors available https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/stream/Collectors.html).
What are the features of Streams compared to Collections in Java
Streams | Collections | |
---|---|---|
Storage | Streams don’t contain data, they have no storage | Collections are data structure, they stores elements. |
Functional | Streams are functional by nature, they don’t modify the source. | Some collections allow the sources to be modified. |
Laziness | Streams have lazy operations (e.g. find first) that allow optimization. | Collections have lazy functions, but they work with all the data. |
Unlimited | Streams could operate with an endless data source. | Collection have by nature finite size. |
Consumable | Elements of a Stream are visited only once. | Elements of collections can be visited multiple time. |
Notes
Most of these answers are extrapolated from the official Java documentation.
Lambda - What are functional interfaces?
Are interfaces with only one abstract method.
interface ExampleInterface {
void myMethod(String parameter);
}
You can add the decorator @FunctionalInterface
if you want a compile check of your function to reduce the risk of runtime errors.
If you are doing an interview and the interviewer want to really challenge you he could ask if this is a functional interface:
@FunctionalInterface
interface ExampleInterface {
boolean equals(Object object);
}
This is not a functional interface because equals
is already a member of Object
and the interface doesn't declare anything new (aside from methods of Object)
. The Java Specification goes more in detail.
The functional interface can have more methods, but only one can be an abstract non-public Object method, i.e. Comparator
is a functional interface:
interface Comparator<T> {
boolean equals(Object obj);
int compare(T o1, T o2);
}
Components of a Lambda expression
FunctionalInterface
Consumervoid accept(Object)
. Consumer is used to iterate with lambdas.
Predicateboolean test(Object)
. Predicate is used to filter with inside lambda expressions.
SupplierT get()
.
Supplier is used to filter with inside lambda expressions. It's most practical usage for us developers is with Logger
and Optional, orElseGet(Supplier supplier).
The lambda expression has 3 parts
- parameters
- arrow
- body
(parameters) →-> {body}
Lambda Syntax examples
// (parameters) -> { return result; }
// It's like an anonymous method
(n) -> { return n; }
// with a single statement, the curly brackets and return are optional
(n) -> n;
// with only 1 parameter the brackets are not mandatory
n -> n;
// in absence of a parameter the brackets are mandatory
() -> 5;