Streams are added to Java APIs to let you manipulate collections of data in a declarative way ( represent in the query instead of writing code).
Problem Statement
Give me all the car names sorted by price and are red in color.
Code without streams
List<Car> filteredCars = new ArrayList<>();
for(Car car : cars) {
if("red".equals(car.getColor().toLowerCase())) {
filteredCars.add(car);
}
}
Collections.sort(filteredCars, new Comparator<Car>() {
@Override
public int compare(Car o1, Car o2) {
return o1.getPrice().compareTo(o2.getPrice());
}
});
List<String> carNames = new ArrayList<>();
for(Car car : filteredCars) {
carNames.add(car.getName());
}
Code with streams
List<String> carNames = cars
.stream()
.filter(car -> "red".equals(car.getColor().toLowerCase()))
.sorted(Comparator.comparing(Car::getPrice))
.map(Car::getName)
.collect(Collectors.toList());
Operation pipeline
Stream
A sequence of elements from a source that supports data processing.
Components:
Source
Sequence of elements
Data processing operations
Characteristics
Pipelining: Most of the stream operations return stream itself allowing to form a larger pipeline.
Internal iteration.
List<String> words = Arrays.asList("Streams", "in", "", " ", "Java");
List<String> result = words
.stream()
.filter(val -> !val.isBlank())
.map(String::toUpperCase)
.limit(2)
.collect(Collectors.toList());
System.out.println(result);
In the above code, there are few operations
Filter: Exclude certain elements from the stream.
Map: Transform an element into another.
Limit: truncate streams to contain no more than a given number.
Collect: Convert a stream into another form.
Stream data processing flow
Stream vs Collection
Collection: Collection is an in-memory data structure that holds all the values.
Stream: Elements are computed on demand.
Traverse only once
A stream can be traversed only once. Trying to traverse more than one will throw an Illegal state exception.
List<String> words = Arrays.asList("Learning", "Java", "Streams", "");
Stream<String> s = words.stream();
s.forEach(System.out::println);
s.forEach(System.out::println); // will throw exception
External vs Internal Iteration
Collection interface requires iteration to be done by the user, this is known as external iteration
Stream library does iteration for you, this is internal iteration
Benefits of internal iteration
The library has the capability for parallel processing.
Optimize the processing algorithm.
Code: External iteration vs Internal Iteration
List<String> words = Arrays.asList("Life", "is", "wonderful");
// External foreach iteration
System.out.print("External iteration (foreach): ");
for(String word: words) {
System.out.print(word + " ");
}
System.out.println();
// External iterator iteration
Iterator<String> iterator = words.iterator();
System.out.print("External iteration (iterator) : ");
while(iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println();
// Internal iteration
System.out.println("Internal iteration : ");
List<String> upperCase = words.stream().map(String::toUpperCase).collect(Collectors.toList());
Stream Operations
There are two groups of operations in the stream:
Intermediate Operation: They connect together to form a pipeline. Eg. filter, map, limit.
Terminal Operation: It causes the pipeline to be executed and close after execution. Eg. collect.
Intermediate operations are lazy. They don’t perform any processing until the terminal operation is invoked on the stream pipeline.
Source: Medium
The Tech Platform
Comments