Java Streams are part of the Java Collections Framework and were introduced in Java 8. They provide a powerful way to process data in a functional style, making it easier to work with collections and perform tasks like filtering, mapping, and reducing. Streams enable concise, declarative, and readable code.
Here’s a breakdown of key concepts and features of Java Streams:
1. What is a Stream?
A Stream is a sequence of elements supporting various operations to process data. It doesn’t store data itself but operates on the source (e.g., a collection, array, or I/O channel).
Key Characteristics:
- Lazy Evaluation: Operations are executed only when a terminal operation is invoked.
- Pipeline Processing: Operations can be chained (filter, map, etc.), forming a pipeline.
- Immutable: The original source is not modified.
2. How to Create a Stream
Streams can be created in several ways:
import java.util.*;
import java.util.stream.*;
public class StreamExamples {
public static void main(String[] args) {
// From a collection
List<String> list = Arrays.asList("A", "B", "C");
Stream<String> stream1 = list.stream();
// From an array
String[] array = {"X", "Y", "Z"};
Stream<String> stream2 = Arrays.stream(array);
// From a range of numbers
IntStream stream3 = IntStream.range(1, 5); // 1, 2, 3, 4
// From static methods
Stream<String> stream4 = Stream.of("Hello", "World");
// Infinite streams
Stream<Integer> stream5 = Stream.iterate(0, n -> n + 2); // 0, 2, 4, 6, ...
Stream<Double> stream6 = Stream.generate(Math::random);
}
}
3. Types of Stream Operations
Intermediate Operations
- These return a new stream and are lazy (not executed until a terminal operation is invoked).
- Common Intermediate Operations:
-
filter(Predicate)
: Filters elements based on a condition. -
map(Function)
: Transforms each element. -
sorted()
/sorted(Comparator)
: Sorts elements. -
distinct()
: Removes duplicates. -
limit(n)
/skip(n)
: Limits/skips elements.
-
Example:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0) // Keep even numbers
.map(n -> n * n) // Square each number
.collect(Collectors.toList()); // Collect to a list
System.out.println(evenNumbers); // Output: [4, 16]
Terminal Operations
- These produce a result or a side effect.
- Common Terminal Operations:
-
collect(Collector)
: Collects elements into a collection. -
forEach(Consumer)
: Performs an action for each element. -
reduce(BinaryOperator)
: Reduces the stream to a single value. -
count()
: Counts elements. -
anyMatch()
,allMatch()
,noneMatch()
: Match predicates. -
findFirst()
/findAny()
: Finds an element.
-
Example:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
String result = names.stream()
.filter(name -> name.startsWith("B")) // Keep names starting with "B"
.findFirst()
.orElse("No match");
System.out.println(result); // Output: Bob
4. Stream Collectors
The Collectors
utility class provides ways to collect data from streams.
Examples:
-
To List:
collect(Collectors.toList())
-
To Map:
collect(Collectors.toMap(keyMapper, valueMapper))
-
Joining Strings:
collect(Collectors.joining(", "))
-
Summarizing Statistics:
collect(Collectors.summarizingInt())
Example:
List<String> names = Arrays.asList("John", "Jane", "Jack");
String result = names.stream()
.collect(Collectors.joining(", ")); // "John, Jane, Jack"
System.out.println(result);
5. Parallel Streams
Parallel Streams divide tasks into smaller chunks and process them in parallel using multiple threads.
Example:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
.mapToInt(n -> n * n)
.sum();
System.out.println(sum); // Output: 55
6. Advanced Features
- FlatMap: Flattens nested streams (e.g., converting a list of lists into a single stream).
-
Primitive Streams: Specialized streams for
int
,long
, anddouble
(e.g.,IntStream
,DoubleStream
). - Custom Collectors: Create custom reduction operations.
Example of flatMap
:
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("A", "B"),
Arrays.asList("C", "D")
);
List<String> flatList = nestedList.stream()
.flatMap(List::stream) // Flatten nested lists
.collect(Collectors.toList());
System.out.println(flatList); // Output: [A, B, C, D]
7. Best Practices
- Use parallel streams only when operations are computationally intensive and thread-safe.
- Avoid modifying the original collection in stream pipelines.
- Use method references (e.g.,
Class::method
) for concise code.
Top comments (0)