Stop Writing For Loops: Master Java Streams with 5 Essential Patterns
I used to think Java Streams were confusing magic. I avoided them for months, writing nested for loops like it was 2005. Then I saw a senior developer calculate cart totals in ONE line of code that took me 15 lines. I felt like a caveman discovering fire. π₯ Let me teach you the 5 patterns that cover 90% of real-world Stream usageβno PhD required.
The Moment I Realized I Was Living in the Pastβ
Picture this: Code review time. I proudly submitted this masterpiece:
// My "beautiful" code
public Integer calculateTotalItems(List<CartItemDTO> items) {
Integer totalItems = 0;
for (CartItemDTO item : items) {
totalItems += item.getQuantity();
}
return totalItems;
}
public BigDecimal calculateTotalPrice(List<CartItemDTO> items) {
BigDecimal totalPrice = BigDecimal.ZERO;
for (CartItemDTO item : items) {
totalPrice = totalPrice.add(item.getSubtotal());
}
return totalPrice;
}
Senior developer's feedback: "You know Streams exist, right?"
His version:
// One-liner magic
Integer totalItems = items.stream()
.mapToInt(CartItemDTO::getQuantity)
.sum();
BigDecimal totalPrice = items.stream()
.map(CartItemDTO::getSubtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
My reaction: "What sorcery is this?!" π€―
His response: "It's called Java 8. Been around since 2014."
What Are Java Streams? (The Simple Explanation)β
The Assembly Line Analogy πβ
Think of a Stream as an assembly line in a factory:
Raw Materials β Conveyor Belt β Processing Stations β Final Product
(List) (stream()) (operations) (collect())
Example:
Shopping Cart Items β stream() β filter() β map() β collect() β Product IDs
Each "station" (method) does ONE job:
- filter() - Quality control (keep only good items)
- map() - Transform items (extract specific part)
- reduce() - Combine items (add them up)
- collect() - Package final result
Why Streams Matterβ
Old Way Problems:
// β Verbose and repetitive
List<Long> productIds = new ArrayList<>();
for (CartItemDTO item : items) {
if (item.getQuantity() > 0) {
productIds.add(item.getProductId());
}
}
Problems:
- β 5 lines for simple operation
- β Hard to read intent
- β Easy to make mistakes (off-by-one, wrong variable)
- β Can't run in parallel easily
Stream Way:
// β
Clear and concise
List<Long> productIds = items.stream()
.filter(item -> item.getQuantity() > 0)
.map(CartItemDTO::getProductId)
.collect(Collectors.toList());
Benefits:
- β 4 lines β readable
- β Intent is crystal clear
- β Less error-prone
- β
Can easily parallelize with
.parallelStream()
Stream Fundamentals: The Building Blocksβ
The Stream Pipelineβ
Every Stream operation follows this pattern:
Source β Intermediate Operations β Terminal Operation β Result
Example:
items.stream() // β Source: Start the pipeline
.filter(item -> item.getQuantity() > 0) // β Intermediate: Process
.map(CartItemDTO::getProductId) // β Intermediate: Process
.collect(Collectors.toList()); // β Terminal: End & get result
Key Concept: Lazy Evaluationβ
Streams don't do anything until you ask for a result!
// This does NOTHING (no terminal operation)
items.stream()
.filter(item -> item.getQuantity() > 0)
.map(CartItemDTO::getProductId);
// β No result, nothing happened
// This EXECUTES (has terminal operation)
List<Long> ids = items.stream()
.filter(item -> item.getQuantity() > 0)
.map(CartItemDTO::getProductId)
.collect(Collectors.toList()); // β Terminal operation triggers execution
The 5 Essential Patterns (90% of Real-World Usage)β
Pattern 1: FILTERING πβ
Use Case: Keep only items that match a condition
Real-World Scenario: "Show me only items that are in stock"
Basic Filterβ
// What you want: "Give me only items with quantity > 0"
List<CartItemDTO> inStockItems = items.stream()
.filter(item -> item.getQuantity() > 0)
.collect(Collectors.toList());
Breaking it down:
filter(item -> item.getQuantity() > 0)- Keep items where quantity > 0item ->- Lambda: For each item, do this checkcollect(Collectors.toList())- Gather results into a List
Old Way Comparison:
// Old Java (pre-Java 8)
List<CartItemDTO> inStockItems = new ArrayList<>();
for (CartItemDTO item : items) {
if (item.getQuantity() > 0) {
inStockItems.add(item);
}
}
Notice: Stream version reads like English: "Filter items by quantity greater than zero"
More Filter Examplesβ
// Example 1: Only premium items
List<CartItemDTO> premiumItems = items.stream()
.filter(CartItemDTO::isPremium) // β Method reference shorthand
.collect(Collectors.toList());
// Example 2: Only items cheaper than $50
List<CartItemDTO> affordableItems = items.stream()
.filter(item -> item.getPrice().compareTo(new BigDecimal("50")) < 0)
.collect(Collectors.toList());
// Example 3: Multiple conditions (AND)
List<CartItemDTO> premiumInStock = items.stream()
.filter(item -> item.getQuantity() > 0 && item.isPremium())
.collect(Collectors.toList());
// Example 4: Multiple conditions (OR)
List<CartItemDTO> specialItems = items.stream()
.filter(item -> item.isPremium() || item.isOnSale())
.collect(Collectors.toList());
// Example 5: Complex conditions
List<CartItemDTO> qualifiedItems = items.stream()
.filter(item -> {
boolean inStock = item.getQuantity() > 0;
boolean affordable = item.getPrice().compareTo(new BigDecimal("100")) < 0;
boolean goodRating = item.getRating() >= 4.0;
return inStock && affordable && goodRating;
})
.collect(Collectors.toList());
When to use: Whenever you need to "keep only items that..."
Pattern 2: TRANSFORMING (Map) πβ
Use Case: Convert each item into something different
Real-World Scenario: "I have cart items, but I only need product IDs"
Basic Mapβ
// What you want: "Give me just the product IDs"
List<Long> productIds = items.stream()
.map(CartItemDTO::getProductId)
.collect(Collectors.toList());
Breaking it down:
map(CartItemDTO::getProductId)- Transform each CartItemDTO into its productId- Result: List of CartItemDTOs β List of Longs
Old Way Comparison:
// Old Java
List<Long> productIds = new ArrayList<>();
for (CartItemDTO item : items) {
productIds.add(item.getProductId());
}
More Map Examplesβ
// Example 1: Get all product names
List<String> productNames = items.stream()
.map(CartItemDTO::getProductName)
.collect(Collectors.toList());
// Example 2: Transform names to uppercase
List<String> upperNames = items.stream()
.map(CartItemDTO::getProductName)
.map(String::toUpperCase) // β Chain transformations!
.collect(Collectors.toList());
// Example 3: Calculate subtotals
List<BigDecimal> subtotals = items.stream()
.map(CartItemDTO::getSubtotal)
.collect(Collectors.toList());
// Example 4: Complex transformation
List<String> itemSummaries = items.stream()
.map(item -> item.getProductName() + " ($" + item.getPrice() + ")")
.collect(Collectors.toList());
// Result: ["Laptop ($1200.00)", "Mouse ($25.00)", ...]
// Example 5: Transform to different DTO
List<ProductSummaryDTO> summaries = items.stream()
.map(item -> new ProductSummaryDTO(
item.getProductId(),
item.getProductName(),
item.getPrice()
))
.collect(Collectors.toList());
When to use: Whenever you need to "convert each item into..."
Pattern 3: COMBINING (Reduce) ββ
Use Case: Combine all items into a single value
Real-World Scenario: "Calculate total price of all items"
Basic Reduceβ
// What you want: "Add up all the subtotals"
BigDecimal totalPrice = items.stream()
.map(CartItemDTO::getSubtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Breaking it down:
map(CartItemDTO::getSubtotal)- Get each item's subtotalreduce(BigDecimal.ZERO, BigDecimal::add)- Start with ZERO, add each subtotal- First parameter: Starting value
- Second parameter: How to combine (the
addmethod)
Old Way Comparison:
// Old Java
BigDecimal totalPrice = BigDecimal.ZERO;
for (CartItemDTO item : items) {
totalPrice = totalPrice.add(item.getSubtotal());
}
More Reduce Examplesβ
// Example 1: Sum quantities (shortcut with mapToInt)
Integer totalQuantity = items.stream()
.mapToInt(CartItemDTO::getQuantity)
.sum(); // β sum() is a built-in shortcut
// Example 2: Find maximum price
BigDecimal maxPrice = items.stream()
.map(CartItemDTO::getPrice)
.reduce(BigDecimal.ZERO, BigDecimal::max);
// Example 3: Concatenate all product names
String allNames = items.stream()
.map(CartItemDTO::getProductName)
.reduce("", (acc, name) -> acc + ", " + name);
// Result: ", Laptop, Mouse, Keyboard"
// Example 4: Concatenate with better formatting
String formattedNames = items.stream()
.map(CartItemDTO::getProductName)
.collect(Collectors.joining(", "));
// Result: "Laptop, Mouse, Keyboard"
// Example 5: Calculate average price
Double avgPrice = items.stream()
.mapToDouble(item -> item.getPrice().doubleValue())
.average()
.orElse(0.0);
// Example 6: Count items (simple way)
long count = items.stream().count();
// Example 7: Custom reduction - multiply all prices (unusual but shows concept)
BigDecimal product = items.stream()
.map(CartItemDTO::getPrice)
.reduce(BigDecimal.ONE, BigDecimal::multiply);
When to use: Whenever you need to "combine all items into one value"
Pattern 4: CHECKING CONDITIONS β β
Use Case: Test if items match a condition
Real-World Scenario: "Does cart have any premium items?"
Basic Checksβ
// Does cart have ANY premium items?
boolean hasPremium = items.stream()
.anyMatch(CartItemDTO::isPremium);
// Are ALL items in stock?
boolean allInStock = items.stream()
.allMatch(item -> item.getQuantity() > 0);
// Does cart have NO premium items?
boolean noPremium = items.stream()
.noneMatch(CartItemDTO::isPremium);
Breaking it down:
anyMatch()- Returns true if at least one item matchesallMatch()- Returns true if all items matchnoneMatch()- Returns true if no items match
Old Way Comparison:
// Old Java - anyMatch
boolean hasPremium = false;
for (CartItemDTO item : items) {
if (item.isPremium()) {
hasPremium = true;
break; // Can stop early
}
}
// Old Java - allMatch
boolean allInStock = true;
for (CartItemDTO item : items) {
if (item.getQuantity() <= 0) {
allInStock = false;
break;
}
}
More Check Examplesβ
// Example 1: Does cart have expensive items (>$100)?
boolean hasExpensive = items.stream()
.anyMatch(item -> item.getPrice().compareTo(new BigDecimal("100")) > 0);
// Example 2: Are all items affordable (<$50)?
boolean allAffordable = items.stream()
.allMatch(item -> item.getPrice().compareTo(new BigDecimal("50")) < 0);
// Example 3: Is cart value over $1000?
boolean over1000 = items.stream()
.map(CartItemDTO::getSubtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add)
.compareTo(new BigDecimal("1000")) > 0;
// Example 4: Does cart have items from specific category?
boolean hasElectronics = items.stream()
.anyMatch(item -> item.getCategory().equals("Electronics"));
// Example 5: Are all items rated 4 stars or higher?
boolean allWellRated = items.stream()
.allMatch(item -> item.getRating() >= 4.0);
// Example 6: Is cart empty or all quantities zero?
boolean effectivelyEmpty = items.isEmpty() || items.stream()
.noneMatch(item -> item.getQuantity() > 0);
When to use: Whenever you need to check "if any...", "if all...", or "if none..."
Pattern 5: FINDING & SORTING πβ
Use Case: Get specific item(s) or sort items
Real-World Scenario: "Find the most expensive item" or "Sort by price"
Finding Itemsβ
// Find the most expensive item
Optional<CartItemDTO> mostExpensive = items.stream()
.max(Comparator.comparing(CartItemDTO::getPrice));
CartItemDTO expensive = mostExpensive.orElse(null); // Handle if not found
// Find the cheapest item
Optional<CartItemDTO> cheapest = items.stream()
.min(Comparator.comparing(CartItemDTO::getPrice));
// Find first premium item
Optional<CartItemDTO> firstPremium = items.stream()
.filter(CartItemDTO::isPremium)
.findFirst();
// Find any premium item (faster in parallel streams)
Optional<CartItemDTO> anyPremium = items.stream()
.filter(CartItemDTO::isPremium)
.findAny();
Breaking it down:
max()/min()- Find largest/smallest based on comparatorfindFirst()- Get first item (returns Optional)findAny()- Get any item (useful in parallel processing)Optional- Container that might have a value or be empty (prevents null errors)
Old Way Comparison:
// Old Java - find max
CartItemDTO mostExpensive = null;
BigDecimal maxPrice = BigDecimal.ZERO;
for (CartItemDTO item : items) {
if (item.getPrice().compareTo(maxPrice) > 0) {
maxPrice = item.getPrice();
mostExpensive = item;
}
}
Sorting Itemsβ
// Sort by price (low to high)
List<CartItemDTO> sortedByPrice = items.stream()
.sorted(Comparator.comparing(CartItemDTO::getPrice))
.collect(Collectors.toList());
// Sort by price (high to low)
List<CartItemDTO> sortedDescending = items.stream()
.sorted(Comparator.comparing(CartItemDTO::getPrice).reversed())
.collect(Collectors.toList());
// Sort by multiple fields: price, then name
List<CartItemDTO> sortedMultiple = items.stream()
.sorted(Comparator.comparing(CartItemDTO::getPrice)
.thenComparing(CartItemDTO::getProductName))
.collect(Collectors.toList());
Limiting and Skippingβ
// Get only first 3 items
List<CartItemDTO> first3 = items.stream()
.limit(3)
.collect(Collectors.toList());
// Skip first 2, get the rest
List<CartItemDTO> skipFirst2 = items.stream()
.skip(2)
.collect(Collectors.toList());
// Pagination: Get items 10-20
List<CartItemDTO> page2 = items.stream()
.skip(10)
.limit(10)
.collect(Collectors.toList());
// Top 5 most expensive items
List<CartItemDTO> top5 = items.stream()
.sorted(Comparator.comparing(CartItemDTO::getPrice).reversed())
.limit(5)
.collect(Collectors.toList());
More Finding Examplesβ
// Example 1: Find item by product ID
Optional<CartItemDTO> found = items.stream()
.filter(item -> item.getProductId().equals(5L))
.findFirst();
// Example 2: Get distinct product categories
List<String> categories = items.stream()
.map(CartItemDTO::getCategory)
.distinct() // β Remove duplicates
.collect(Collectors.toList());
// Example 3: Check if item exists
boolean exists = items.stream()
.anyMatch(item -> item.getProductId().equals(5L));
// Example 4: Get top 3 rated items
List<CartItemDTO> topRated = items.stream()
.sorted(Comparator.comparing(CartItemDTO::getRating).reversed())
.limit(3)
.collect(Collectors.toList());
When to use: Whenever you need to "find the...", "sort by...", or "get first N..."
Combining Patterns: Real-World Examplesβ
Example 1: E-Commerce Cart Totalβ
Requirement: Calculate total price of in-stock items only
BigDecimal total = items.stream()
.filter(item -> item.getQuantity() > 0) // Pattern 1: Filter
.map(CartItemDTO::getSubtotal) // Pattern 2: Transform
.reduce(BigDecimal.ZERO, BigDecimal::add); // Pattern 3: Reduce
Old Way:
BigDecimal total = BigDecimal.ZERO;
for (CartItemDTO item : items) {
if (item.getQuantity() > 0) {
total = total.add(item.getSubtotal());
}
}
Example 2: Premium Items Summaryβ
Requirement: Get names of top 3 most expensive premium items
List<String> topPremium = items.stream()
.filter(CartItemDTO::isPremium) // Pattern 1: Filter
.sorted(Comparator.comparing(CartItemDTO::getPrice).reversed()) // Pattern 5: Sort
.limit(3) // Pattern 5: Limit
.map(CartItemDTO::getProductName) // Pattern 2: Transform
.collect(Collectors.toList());
Old Way:
List<CartItemDTO> premiumItems = new ArrayList<>();
for (CartItemDTO item : items) {
if (item.isPremium()) {
premiumItems.add(item);
}
}
premiumItems.sort((a, b) -> b.getPrice().compareTo(a.getPrice()));
List<String> topPremium = new ArrayList<>();
for (int i = 0; i < Math.min(3, premiumItems.size()); i++) {
topPremium.add(premiumItems.get(i).getProductName());
}
Example 3: Validation Checkβ
Requirement: Check if cart is valid (all items in stock and total under $10,000)
boolean allInStock = items.stream()
.allMatch(item -> item.getQuantity() > 0); // Pattern 4: Check
BigDecimal total = items.stream()
.map(CartItemDTO::getSubtotal) // Pattern 2: Transform
.reduce(BigDecimal.ZERO, BigDecimal::add); // Pattern 3: Reduce
boolean validCart = allInStock &&
total.compareTo(new BigDecimal("10000")) < 0;
Example 4: Product Statisticsβ
Requirement: Get average price of electronics
Double avgElectronicsPrice = items.stream()
.filter(item -> item.getCategory().equals("Electronics")) // Pattern 1: Filter
.mapToDouble(item -> item.getPrice().doubleValue()) // Pattern 2: Transform
.average() // Pattern 3: Reduce
.orElse(0.0);
Example 5: Complex Business Logicβ
Requirement: Apply 10% discount to items over $100, then calculate total
BigDecimal discountedTotal = items.stream()
.map(item -> {
BigDecimal price = item.getPrice();
if (price.compareTo(new BigDecimal("100")) > 0) {
// Apply 10% discount
return price.multiply(new BigDecimal("0.9"))
.multiply(new BigDecimal(item.getQuantity()));
}
return item.getSubtotal();
})
.reduce(BigDecimal.ZERO, BigDecimal::add);
Quick Reference Guideβ
When to Use Which Patternβ
| What You Need | Pattern | Method |
|---|---|---|
| Keep matching items | Filter | .filter() |
| Convert items | Transform | .map() |
| Calculate total/sum | Reduce | .reduce() / .sum() |
| Check if any/all match | Check | .anyMatch() / .allMatch() |
| Find max/min | Find | .max() / .min() |
| Sort items | Sort | .sorted() |
| Get first N items | Limit | .limit() |
| Get average | Reduce | .average() |
| Count items | Reduce | .count() |
Common Terminal Operationsβ
.collect(Collectors.toList()) // β List
.collect(Collectors.toSet()) // β Set
.reduce() // β Single value
.sum() // β Number
.count() // β long
.average() // β OptionalDouble
.min() / .max() // β Optional
.anyMatch() / .allMatch() // β boolean
.findFirst() / .findAny() // β Optional
.forEach() // β void (side effects)
Performance Tips & Best Practicesβ
β Do Thisβ
// 1. Use method references when possible
items.stream()
.map(CartItemDTO::getProductName) // β
Clean
.collect(Collectors.toList());
// 2. Use mapToInt/mapToDouble for primitives (better performance)
int total = items.stream()
.mapToInt(CartItemDTO::getQuantity) // β
Primitive stream
.sum();
// 3. Short-circuit operations
boolean hasExpensive = items.stream()
.anyMatch(item -> item.getPrice().compareTo(new BigDecimal("100")) > 0);
// β
Stops at first match, doesn't check all items
// 4. Use parallel streams for large datasets (1000+ items)
BigDecimal total = items.parallelStream()
.map(CartItemDTO::getSubtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
β Don't Do Thisβ
// 1. Don't use lambdas when method reference works
items.stream()
.map(item -> item.getProductName()) // β Verbose
.collect(Collectors.toList());
// 2. Don't modify external variables (side effects)
List<String> names = new ArrayList<>();
items.stream()
.forEach(item -> names.add(item.getProductName())); // β Bad practice
// Use collect instead:
List<String> names = items.stream()
.map(CartItemDTO::getProductName)
.collect(Collectors.toList()); // β
Functional
// 3. Don't reuse streams
Stream<CartItemDTO> stream = items.stream();
long count = stream.count();
// stream.collect(Collectors.toList()); // β IllegalStateException
// Create new stream for each operation:
long count = items.stream().count(); // β
List<CartItemDTO> list = items.stream().collect(Collectors.toList()); // β
// 4. Don't use parallel for small datasets or order-dependent operations
items.parallelStream() // β Overhead not worth it for <1000 items
.limit(10)
.collect(Collectors.toList());
How to Approach New Problemsβ
When facing a new requirement, ask yourself:
Step 1: What do I need?β
| Need | Pattern |
|---|---|
| "Keep only items that..." | β Filter |
| "Convert each item to..." | β Map |
| "Calculate total/sum of..." | β Reduce |
| "Check if any/all..." | β Check |
| "Find the max/min..." | β Find |
| "Sort items by..." | β Sort |
Step 2: Combine patternsβ
Most real problems use multiple patterns:
Filter β Map β Reduce
Filter β Sort β Limit
Filter β Map β Collect
Step 3: Practiceβ
Convert your existing for loops to Streams:
Before:
List<String> result = new ArrayList<>();
for (Item item : items) {
if (item.isValid()) {
result.add(item.getName());
}
}
After:
List<String> result = items.stream()
.filter(Item::isValid)
.map(Item::getName)
.collect(Collectors.toList());
Common Mistakes to Avoidβ
Mistake 1: Forgetting Terminal Operationβ
// β This does NOTHING
items.stream()
.filter(item -> item.getQuantity() > 0)
.map(CartItemDTO::getProductId);
// β
Add terminal operation
List<Long> ids = items.stream()
.filter(item -> item.getQuantity() > 0)
.map(CartItemDTO::getProductId)
.collect(Collectors.toList()); // β Terminal operation
Mistake 2: Not Handling Optionalβ
// β Can throw NoSuchElementException
CartItemDTO item = items.stream()
.max(Comparator.comparing(CartItemDTO::getPrice))
.get(); // β Dangerous if list is empty
// β
Handle Optional properly
CartItemDTO item = items.stream()
.max(Comparator.comparing(CartItemDTO::getPrice))
.orElse(null); // or .orElseThrow() or .orElseGet()
Mistake 3: Modifying Source Collectionβ
// β ConcurrentModificationException
items.stream()
.filter(item -> item.getQuantity() == 0)
.forEach(items::remove); // β Don't modify source during stream
// β
Collect items to remove first
List<CartItemDTO> toRemove = items.stream()
.filter(item -> item.getQuantity() == 0)
.collect(Collectors.toList());
items.removeAll(toRemove);
Summary: The 5 Patternsβ
Pattern Cheat Sheetβ
| # | Pattern | Use For | Key Methods |
|---|---|---|---|
| 1 | Filter | Keep matching items | .filter() |
| 2 | Transform | Convert items | .map(), .mapToInt() |
| 3 | Reduce | Combine into one value | .reduce(), .sum(), .average() |
| 4 | Check | Test conditions | .anyMatch(), .allMatch(), .noneMatch() |
| 5 | Find/Sort | Get specific items | .max(), .min(), .sorted(), .limit() |
Why Streams Matterβ
Before Streams (Java 7):
- β Verbose for loops
- β Error-prone manual state management
- β Hard to parallelize
- β Intent hidden in implementation
After Streams (Java 8+):
- β Concise, readable code
- β Functional, immutable approach
- β Easy parallelization
- β Intent expressed clearly
Practice Challenge π―β
Convert these for loops to Streams:
Challenge 1β
// Find all items over $50
List<CartItemDTO> expensive = new ArrayList<>();
for (CartItemDTO item : items) {
if (item.getPrice().compareTo(new BigDecimal("50")) > 0) {
expensive.add(item);
}
}
Solution
List<CartItemDTO> expensive = items.stream()
.filter(item -> item.getPrice().compareTo(new BigDecimal("50")) > 0)
.collect(Collectors.toList());
Challenge 2β
// Get product IDs of premium items
List<Long> premiumIds = new ArrayList<>();
for (CartItemDTO item : items) {
if (item.isPremium()) {
premiumIds.add(item.getProductId());
}
}
Solution
List<Long> premiumIds = items.stream()
.filter(CartItemDTO::isPremium)
.map(CartItemDTO::getProductId)
.collect(Collectors.toList());
Challenge 3β
// Calculate average price of electronics
double sum = 0;
int count = 0;
for (CartItemDTO item : items) {
if (item.getCategory().equals("Electronics")) {
sum += item.getPrice().doubleValue();
count++;
}
}
double avg = count > 0 ? sum / count : 0;
Solution
double avg = items.stream()
.filter(item -> item.getCategory().equals("Electronics"))
.mapToDouble(item -> item.getPrice().doubleValue())
.average()
.orElse(0.0);
What's Next?β
Now that you've mastered the 5 core patterns, explore:
- Advanced collectors -
Collectors.groupingBy(),partitioningBy() - Parallel streams - When and how to use
.parallelStream() - Custom collectors - Create your own collection strategies
- Optional API - Deep dive into handling null safely
- Stream debugging - Using
.peek()for debugging
Questions about Streams? Drop them in the comments! π
Converted a gnarly for loop to Streams? Share your before/after! π
Found this helpful? Share it with developers still living in 2005! π
