Streams in Java 8 provide a powerful way to work with sequences of data in a declarative and functional style. Below is a comprehensive cheat sheet that covers key concepts, methods, and examples.
1. Creating Streams
- From a Collection:
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
- From Arrays:
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
- From Values:
Stream<String> stream = Stream.of("a", "b", "c");
- Empty Stream:
Stream<String> emptyStream = Stream.empty();
- Generate Infinite Streams (using
Stream.generate
andStream.iterate
):
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
2. Stream Operations
Stream operations are divided into intermediate (lazy) and terminal (eager).
Intermediate Operations (Lazy Evaluation)
These operations return a new stream and do not trigger processing until a terminal operation is invoked.
- Filter (selects elements based on a condition):
stream.filter(s -> s.startsWith("a"))
.forEach(System.out::println);
- Map (transforms elements):
stream.map(String::toUpperCase)
.forEach(System.out::println);
- Distinct (removes duplicates):
stream.distinct()
.forEach(System.out::println);
- Sorted (sorts elements):
stream.sorted()
.forEach(System.out::println);
- Peek (intermediate operation for debugging, without consuming the stream):
stream.peek(System.out::println)
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
- Limit (limits the number of elements):
stream.limit(3)
.forEach(System.out::println);
- Skip (skips the first N elements):
stream.skip(2)
.forEach(System.out::println);
- FlatMap (flatten nested collections):
Stream<List<String>> nested = Stream.of(Arrays.asList("a", "b"), Arrays.asList("c", "d"));
nested.flatMap(Collection::stream)
.forEach(System.out::println);
Terminal Operations (Eager Evaluation)
These operations trigger the processing of the stream and produce a result.
- ForEach (perform an action for each element):
stream.forEach(System.out::println);
- Collect (collect results into a collection or other types):
List<String> result = stream.collect(Collectors.toList());
Set<String> resultSet = stream.collect(Collectors.toSet());
- Count (counts the elements):
long count = stream.count();
- Reduce (reduces the stream to a single value):
Optional<String> concatenated = stream.reduce((s1, s2) -> s1 + s2);
- Max/Min (finds the maximum or minimum based on a comparator):
Optional<String> max = stream.max(String::compareTo);
Optional<String> min = stream.min(String::compareTo);
- AnyMatch/AllMatch/NoneMatch (predicate tests):
boolean anyMatch = stream.anyMatch(s -> s.startsWith("a"));
boolean allMatch = stream.allMatch(s -> s.length() == 1);
boolean noneMatch = stream.noneMatch(s -> s.contains("x"));
- FindFirst/FindAny (returns an element based on conditions):
Optional<String> first = stream.findFirst();
Optional<String> any = stream.findAny();
3. Collectors Utility Class
Collectors are commonly used with the collect()
method to accumulate elements into collections, summarizers, or grouping operations.
- toList (collect into a List):
List<String> list = stream.collect(Collectors.toList());
- toSet (collect into a Set):
Set<String> set = stream.collect(Collectors.toSet());
- joining (concatenate elements into a String):
String result = stream.collect(Collectors.joining(", "));
- groupingBy (group by a classifier):
Map<Integer, List<String>> groupedByLength = stream.collect(Collectors.groupingBy(String::length));
- partitioningBy (split into two groups based on a predicate):
Map<Boolean, List<String>> partitioned = stream.collect(Collectors.partitioningBy(s -> s.length() > 2));
- counting (count the elements):
long count = stream.collect(Collectors.counting());
- summarizingInt (summary statistics for an int-valued property):
IntSummaryStatistics stats = stream.collect(Collectors.summarizingInt(String::length));
- reducing (aggregate elements using a reduction operation):
Optional<String> concatenated = stream.collect(Collectors.reducing((s1, s2) -> s1 + s2));
4. Working with Optional
Optional
is a container that may or may not contain a value.
- Creating an Optional:
Optional<String> optional = Optional.of("Hello");
Optional<String> emptyOptional = Optional.empty();
- If Present:
optional.ifPresent(System.out::println);
- Get the value (with a default):
String value = optional.orElse("Default");
String valueWithException = optional.orElseThrow(() -> new IllegalArgumentException("Value missing"));
- Map and FlatMap (transforming the value):
Optional<String> upper = optional.map(String::toUpperCase);
5. Parallel Streams
Streams can be processed in parallel for potential performance improvements.
- Using Parallel Stream:
stream.parallel().forEach(System.out::println);
- Collecting Results from Parallel Streams:
List<String> result = stream.parallel().collect(Collectors.toList());
Note: Parallel streams can be more efficient for large datasets but come with overhead, so they should be used judiciously.
6. Example
List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
List<String> filteredWords = words.stream()
.filter(w -> w.length() > 5)
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
filteredWords.forEach(System.out::println);
This will output:
BANANA
CHERRY
ELDERBERRY
7. Common Pitfalls
- Nulls in Streams: Nulls are generally not allowed in streams, especially when working with collectors.
- Stateful Operations: Operations like
distinct()
andsorted()
may require additional memory and could affect performance. - Ordering: Some stream operations like
sorted()
andforEachOrdered()
preserve ordering, butparallel()
can cause non-deterministic order unless properly managed.
8. Best Practices
- Use method references (
::
) where possible for readability. - Avoid using side effects inside streams. Keep the operations as pure as possible.
- Prefer functional programming style over mutable operations like
for
loops.
This cheat sheet covers the essential aspects of Java 8 streams, but there’s more to explore depending on your use case. The official Java Streams API documentation is a great place to dive deeper.