Java Streams have revolutionized the way we handle collections and streams of data by providing a rich API for performing complex data processing operations in a functional style. Here’s a breakdown of the essential intermediate and terminal operations, complete with examples to help you master data processing in Java.
Intermediate Operations
filter(Predicate<T>)
Filters elements based on a condition.
List<String> names = Arrays.asList("Anna", "Bob", "Charlie", "Diana");
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
// Output: [Anna]
map(Function<T, R>)
Transforms elements using a provided function.
List<String> names = Arrays.asList("anna", "bob", "charlie");
List<String> capitalizedNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// Output: [ANNA, BOB, CHARLIE]
flatMap(Function<T, Stream<R>>)
Flattens nested Stream
objects into a single Stream
.
List<List<String>> listOfLists = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d")
);
List<String> flatList = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// Output: [a, b, c, d]
Head you can read more on FlatMap
https://codetechsummit.com/flatmap-java-8-streams/
distinct()
Removes duplicates.
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
// Output: [1, 2, 3, 4, 5]
sorted()
Sorts elements.
List<Integer> numbers = Arrays.asList(4, 2, 3, 1, 5);
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
// Output: [1, 2, 3, 4, 5]
peek(Consumer<T>)
Performs an action for each element without altering the stream.
List<Integer> numbers = Arrays.asList(1, 2, 3);
numbers.stream()
.peek(number -> System.out.println("From stream: " + number))
.collect(Collectors.toList());
// Output: From stream: 1
// From stream: 2
// From stream: 3
limit(long n)
Limits the number of elements.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> limitedNumbers = numbers.stream()
.limit(3)
.collect(Collectors.toList());
// Output: [1, 2, 3]
skip(long n)
Skips the first n
elements.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> skippedNumbers = numbers.stream()
.skip(2)
.collect(Collectors.toList());
// Output: [3, 4, 5]
parallel()
Processes elements in parallel.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();
// Output: Sum of numbers
sequential()
Processes elements sequentially (useful after parallel()).
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.parallelStream()
.sequential()
.count();
// Output: 5
unordered()
Hints that the order of elements is not significant.
Set<Integer> numbers = new HashSet<>(Arrays.asList(5, 4, 3, 2, 1));
List<Integer> unorderedList = numbers.stream()
.unordered()
.collect(Collectors.toList());
// Output: [Any order]
Terminal Operations
forEach(Consumer<T>)
Performs an action for each element.
List<String> names = Arrays.asList("Anna", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));
// Output: Anna
// Bob
// Charlie
forEachOrdered(Consumer<T>)
Ensures the action is performed in the encounter order, important for parallel streams.
List<String> names = Arrays.asList("Anna", "Bob", "Charlie");
names.parallelStream()
.forEachOrdered(System.out::println);
// Output: Anna
// Bob
// Charlie
toArray()
Converts the stream into an array.
List<String> names = Arrays.asList("Anna", "Bob", "Charlie");
String[] namesArray = names.stream()
.toArray(String[]::new);
// Output: Array of names
reduce(BinaryOperator<T>)
Performs a reduction on the elements.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, Integer::sum);
// Output: 15
collect(Collector<T,A,R>)
Collects elements into a container.
List<String> names = Arrays.asList("Anna", "Bob", "Charlie");
String concatenatedNames = names.stream()
.collect(Collectors.joining(", "));
// Output: Anna, Bob, Charlie
min(Comparator<T>)
and max(Comparator<T>)
Finds the minimum or maximum element.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> min = numbers.stream()
.min(Integer::compare);
Optional<Integer> max = numbers.stream()
.max(Integer::compare);
// Output: min.isPresent() ? min.get() : "No min"
// max.isPresent() ? max.get() : "No max"
count()
Counts the elements.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream()
.count();
// Output: 5
anyMatch(Predicate<T>)
, allMatch(Predicate<T>)
, noneMatch(Predicate<T>)
Evaluates elements based on a predicate.
List<String> names = Arrays.asList("Anna", "Bob", "Charlie");
boolean anyMatchResult = names.stream()
.anyMatch(name -> name.startsWith("A"));
boolean allMatchResult = names.stream()
.allMatch(name -> name.length() > 3);
boolean noneMatchResult = names.stream()
.noneMatch(name -> name.endsWith("z"));
// Output: anyMatchResult, allMatchResult, noneMatchResult
findFirst()
and findAny()
Finds an element.
List<String> names = Arrays.asList("Anna", "Bob", "Charlie");
Optional<String> firstFound = names.stream()
.findFirst();
Optional<String> anyFound = names.stream()
.findAny();
// Output: firstFound.isPresent() ? firstFound.get() : "No element"
// anyFound.isPresent() ? anyFound.get() : "No element"
Java’s Stream API is a powerful tool that enables developers to write clean, concise, and efficient data processing code. By understanding and utilizing the various intermediate and terminal operations, you can unlock the full potential of this API in your Java applications.