Java Streams: Comparing Collection Processing in Java 7 and Java 8
This article demonstrates how Java 8 streams simplify collection processing compared to Java 7 by showing code examples that filter, sort, map, and reduce dishes, explains stream concepts, intermediate and terminal operations, and provides practical usage tips for parallel processing and common stream methods.
Introduction
The article starts with a code snippet that contrasts how Java 7 and Java 8 handle collection processing, using a Dish class with calories and name fields. The goal is to filter dishes with fewer than 400 calories, sort them by calorie count, and return their names.
Java 7 Implementation
public static List<String> getLowCaloricDishesNamesInJava7(List<Dish> dishes){
List<Dish> lowCaloricDishes = new ArrayList<>();
for(Dish d: dishes){
if(d.getCalories() < 400){
lowCaloricDishes.add(d);
}
}
List<String> lowCaloricDishesName = new ArrayList<>();
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
public int compare(Dish d1, Dish d2){
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
for(Dish d: lowCaloricDishes){
lowCaloricDishesName.add(d.getName());
}
return lowCaloricDishesName;
}Java 8 Implementation
public static List<String> getLowCaloricDishesNamesInJava8(List<Dish> dishes){
return dishes.stream()
.filter(d -> d.getCalories() < 400)
.sorted(comparing(Dish::getCalories))
.map(Dish::getName)
.collect(toList());
}For parallel processing, simply replace dishes.stream() with dishes.parallelStream().
Understanding Streams
What Is a Stream?
A Stream is a new member of the Java API that allows declarative processing of collections, supports method chaining, and can be executed in parallel, making collection handling more efficient and readable.
Differences Between Streams and Collections
Collections store and access elements with defined time/space complexity; streams are primarily for element computation.
Elements can be added or removed from collections, but streams cannot modify their source.
Stream elements are computed lazily, only when needed; collection elements must be fully materialized beforehand.
Streams can be traversed only once; attempting a second traversal throws
java.lang.IllegalStateException: stream has already been operated upon or closed.
List<String> names = Arrays.asList("Java8", "Lambdas", "In", "Action");
Stream<String> s = names.stream();
s.forEach(System.out::println);
s.forEach(System.out::println); // throws IllegalStateExceptionExternal vs. Internal Iteration
Collections use external iteration, requiring the programmer to control the iteration process. Streams use internal iteration, allowing the JVM to choose the most efficient strategy, including parallel execution.
Stream Operations Classification
Stream operations are divided into two categories:
Intermediate Operations
These return another stream and can be chained. They are not executed until a terminal operation is invoked, allowing the runtime to optimize (e.g., merge, short‑circuit).
Terminal Operations
These produce a non‑stream result (e.g., a collection, a value, or Optional) and trigger the processing of the pipeline.
Common Intermediate Operations
filter(Predicate<? super T> predicate)– keeps elements that satisfy the predicate. distinct() – removes duplicates based on hashCode() and equals(). limit(n) – returns the first n elements. skip(n) – discards the first n elements. map(Function<? super T,? extends R> mapper) – transforms each element.
flatMap(Function<? super T,? extends Stream<? extends R>> mapper)– flattens nested streams into a single stream.
List<String> words = Arrays.asList("Hello", "World");
List<String> charList = words.stream()
.map(word -> word.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());Matching and Finding
anyMatch– returns true if any element matches the predicate. allMatch – returns true only if all elements match. noneMatch – returns true if no elements match. findAny – returns an Optional containing any element. findFirst – returns the first element (useful for ordered streams).
List<Integer> nums = Arrays.asList(1,2,3,4,5,6);
if(nums.stream().anyMatch(n -> n % 3 == 0)){
System.out.println("There is a multiple of 3");
}
nums.stream().filter(n -> n > 2).findAny().ifPresent(System.out::println);Reduction
The reduce method aggregates stream elements. reduce(T identity, BinaryOperator<T> accumulator) – e.g., sum of numbers: nums.stream().reduce(0, (a,b) -> a + b).
Method references can compute max/min: nums.stream().reduce(Integer::max), nums.stream().reduce(Integer::min).
Conclusion
Streams enable declarative, chainable, and parallel processing of collections, improving readability and performance.
Stream APIs are divided into intermediate (returning a stream) and terminal (returning a concrete result) operations.
Key stream methods include filter, distinct, limit, skip, map, flatMap, matching methods ( anyMatch, allMatch, noneMatch), finding methods ( findAny, findFirst), and reduction ( reduce).
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
