Java 8 Stream api
1. Introduction to Java Streams
Java 8 introduced the Stream API, which is used to process collections of data
in a functional programming style. Streams provide a way to operate on
collections with declarative, parallel, and efficient execution.
Key Features of Streams:
Introduced in Java 8
Supports functional-style programming
Allows processing of collections efficiently
Supports lazy execution
Can perform filter, map, reduce, sort, and collect operations
Can be sequential or parallel
2. Difference Between Collections and Streams
Feature Collections Streams
Stores data Yes No
Iteration External (explicit looping) Internal (functional)
Modification Allows modification Read-only, does not modify the source
Lazy Execution No Yes
Can be reused? Yes No (Streams are one-time use)
3. How to Create a Stream?
There are multiple ways to create a Stream in Java:
1. From a Collection (List, Set, etc.)
import java.util.*;
Java 8 Stream api 1
import java.util.stream.*;
public class StreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Stream<String> nameStream = names.stream();
nameStream.forEach(System.out::println);
}
}
2. From Arrays
int[] numbers = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(numbers);
3. Using Stream.of()
Stream<String> stream = Stream.of("A", "B", "C");
stream.forEach(System.out::println);
4. Using Stream.generate() (Infinite Stream)
Stream<Double> randomNumbers = Stream.generate(Math::random).limit
(5);
randomNumbers.forEach(System.out::println);
5. Using Stream.iterate()
Java 8 Stream api 2
Stream<Integer> evenNumbers = Stream.iterate(2, n -> n + 2).limit(5);
evenNumbers.forEach(System.out::println);
4. Stream Operations
Stream operations are divided into Intermediate and Terminal operations.
4.1 Intermediate Operations (Transform Streams)
Intermediate operations return a new Stream and are lazy, meaning they are
executed only when a terminal operation is called.
Operation Description Example
Filters elements based on a
filter() list.stream().filter(n -> n > 5)
condition
map() Transforms each element list.stream().map(n -> n * 2)
flatMap() Flattens nested structures listOfLists.stream().flatMap(List::stream)
sorted() Sorts elements list.stream().sorted()
distinct() Removes duplicates list.stream().distinct()
peek() Debugging helper (like forEach ) list.stream().peek(System.out::println)
limit(n) Limits the number of elements list.stream().limit(3)
skip(n) Skips the first n elements list.stream().skip(2)
Example Using Intermediate Operations
java
CopyEdit
List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50);
numbers.stream()
.filter(n -> n > 20)
.map(n -> n * 2)
.sorted()
.forEach(System.out::println);
Java 8 Stream api 3
4.2 Terminal Operations (End Streams)
Terminal operations consume the stream and return a result or side effect.
Operation Description Example
Performs an action for each
forEach() list.stream().forEach(System.out::println)
element
Collects elements into a List,
collect() list.stream().collect(Collectors.toList())
Set, Map, etc.
Reduces elements to a single
reduce() list.stream().reduce(0, Integer::sum)
value
count() Counts elements list.stream().count()
Finds the minimum/maximum
min() / max() list.stream().min(Integer::compareTo)
element
Returns true if any element
anyMatch() list.stream().anyMatch(n -> n > 10)
matches
Returns true if all elements
allMatch() list.stream().allMatch(n -> n > 0)
match
Returns true if no elements
noneMatch() list.stream().noneMatch(n -> n < 0)
match
Example Using Terminal Operations
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");
long count = names.stream()
.filter(name -> name.startsWith("J"))
.count();
System.out.println("Count: " + count);
5. Collecting Results Using Collectors
Collectors help convert streams into different formats.
Collector Description Example
Collect elements
toList() list.stream().collect(Collectors.toList())
into a List
Java 8 Stream api 4
Collect elements
toSet() list.stream().collect(Collectors.toSet())
into a Set
Collect elements
toMap() list.stream().collect(Collectors.toMap(i -> i, i -> i * i))
into a Map
Concatenates
joining() elements into a list.stream().collect(Collectors.joining(", "))
string
Groups elements
groupingBy() based on a list.stream().collect(Collectors.groupingBy(String::length))
condition
Partitions
partitioningBy() elements into list.stream().collect(Collectors.partitioningBy(n -> n > 10))
two groups
Example Using Collectors
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squaredNumbers = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println(squaredNumbers);
6. Parallel Streams
Parallel streams allow processing multiple elements concurrently using multiple
CPU cores.
Sequential Stream Example
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream().forEach(System.out::println);
Parallel Stream Example
Java 8 Stream api 5
numbers.parallelStream().forEach(System.out::println);
When to Use Parallel Streams?
When processing large data sets
When CPU-intensive operations are required
Avoid in small lists (overhead is higher)
7. Important Stream Interview Questions
1. What is the difference between map() and flatMap() ?
map() applies a function to each element and returns a stream of
streams.
flatMap() flattens the streams into a single stream.
2. Why are Streams immutable?
Streams do not modify the original data; they return new transformed
streams.
3. How does lazy execution work in Streams?
Intermediate operations are not executed immediately; they execute
only when a terminal operation is called.
4. How are parallel streams different from sequential streams?
Parallel streams use multiple threads, while sequential streams run in a
single thread.
Java 8 Stream api 6