Intermediate operations in Java Streams are operations that transform a stream into another stream. They are lazy and only executed when a terminal operation is invoked.
1. filter() Operation
Basic filter() Usage
import java.util.*;
import java.util.stream.*;
public class FilterOperation {
public static void main(String[] args) {
System.out.println("=== filter() Operation ===");
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 1. Filter even numbers
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("Even numbers: " + evenNumbers);
// 2. Filter numbers greater than 5
List<Integer> greaterThan5 = numbers.stream()
.filter(n -> n > 5)
.collect(Collectors.toList());
System.out.println("Numbers > 5: " + greaterThan5);
// 3. Filter with multiple conditions
List<Integer> between3And7 = numbers.stream()
.filter(n -> n > 3 && n < 7)
.collect(Collectors.toList());
System.out.println("Numbers between 3 and 7: " + between3And7);
demonstrateStringFiltering();
demonstrateObjectFiltering();
}
public static void demonstrateStringFiltering() {
System.out.println("\n=== String Filtering ===");
List<String> names = Arrays.asList("John", "Alice", "Bob", "Carol", "David", "Eve", "Frank");
// 1. Filter names starting with 'A'
List<String> startsWithA = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
System.out.println("Names starting with 'A': " + startsWithA);
// 2. Filter names with length > 4
List<String> longNames = names.stream()
.filter(name -> name.length() > 4)
.collect(Collectors.toList());
System.out.println("Names longer than 4 characters: " + longNames);
// 3. Filter names containing 'a'
List<String> containsA = names.stream()
.filter(name -> name.toLowerCase().contains("a"))
.collect(Collectors.toList());
System.out.println("Names containing 'a': " + containsA);
// 4. Complex filtering with multiple conditions
List<String> complexFilter = names.stream()
.filter(name -> name.length() >= 4 && name.length() <= 5)
.filter(name -> !name.endsWith("n"))
.collect(Collectors.toList());
System.out.println("Complex filter result: " + complexFilter);
}
public static void demonstrateObjectFiltering() {
System.out.println("\n=== Object Filtering ===");
class Product {
String name;
double price;
String category;
int stock;
public Product(String name, double price, String category, int stock) {
this.name = name;
this.price = price;
this.category = category;
this.stock = stock;
}
public String getName() { return name; }
public double getPrice() { return price; }
public String getCategory() { return category; }
public int getStock() { return stock; }
@Override
public String toString() {
return String.format("Product{name='%-12s', price=$%-6.2f, category='%-10s', stock=%d}",
name, price, category, stock);
}
}
List<Product> products = Arrays.asList(
new Product("Laptop", 999.99, "Electronics", 10),
new Product("Smartphone", 699.99, "Electronics", 25),
new Product("Headphones", 149.99, "Electronics", 0),
new Product("T-Shirt", 19.99, "Clothing", 100),
new Product("Jeans", 49.99, "Clothing", 75),
new Product("Book", 12.99, "Education", 200),
new Product("Desk Lamp", 29.99, "Home", 5),
new Product("Mouse", 25.99, "Electronics", 50)
);
// 1. Filter electronics products
List<Product> electronics = products.stream()
.filter(p -> "Electronics".equals(p.getCategory()))
.collect(Collectors.toList());
System.out.println("Electronics products:");
electronics.forEach(System.out::println);
// 2. Filter products under $50
List<Product> affordableProducts = products.stream()
.filter(p -> p.getPrice() < 50.0)
.collect(Collectors.toList());
System.out.println("\nProducts under $50:");
affordableProducts.forEach(System.out::println);
// 3. Filter products in stock (stock > 0)
List<Product> inStockProducts = products.stream()
.filter(p -> p.getStock() > 0)
.collect(Collectors.toList());
System.out.println("\nProducts in stock:");
inStockProducts.forEach(System.out::println);
// 4. Complex filter: Electronics under $100 with stock
List<Product> specialOffers = products.stream()
.filter(p -> "Electronics".equals(p.getCategory()))
.filter(p -> p.getPrice() < 100.0)
.filter(p -> p.getStock() > 0)
.collect(Collectors.toList());
System.out.println("\nSpecial offers (Electronics < $100 with stock):");
specialOffers.forEach(System.out::println);
// 5. Filter with custom predicate
Predicate<Product> premiumProduct = p ->
p.getPrice() > 500.0 && "Electronics".equals(p.getCategory());
List<Product> premiumProducts = products.stream()
.filter(premiumProduct)
.collect(Collectors.toList());
System.out.println("\nPremium products:");
premiumProducts.forEach(System.out::println);
}
}
Advanced filter() Techniques
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
public class AdvancedFiltering {
public static void main(String[] args) {
System.out.println("=== Advanced Filtering Techniques ===");
List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry",
"fig", "grape", "honeydew", "kiwi");
// 1. Filter with method reference
List<String> shortWords = words.stream()
.filter(AdvancedFiltering::isShortWord) // Method reference
.collect(Collectors.toList());
System.out.println("Short words (<= 5 chars): " + shortWords);
// 2. Filter with composed predicates
Predicate<String> startsWithVowel = s ->
"aeiou".contains(String.valueOf(s.charAt(0)).toLowerCase());
Predicate<String> endsWithVowel = s ->
"aeiou".contains(String.valueOf(s.charAt(s.length() - 1)).toLowerCase());
List<String> startsAndEndsWithVowel = words.stream()
.filter(startsWithVowel.and(endsWithVowel))
.collect(Collectors.toList());
System.out.println("Words starting and ending with vowel: " + startsAndEndsWithVowel);
// 3. Filter with negation
List<String> notStartingWithVowel = words.stream()
.filter(startsWithVowel.negate())
.collect(Collectors.toList());
System.out.println("Words not starting with vowel: " + notStartingWithVowel);
// 4. Dynamic filtering based on conditions
demonstrateDynamicFiltering(words);
// 5. Filter with Optional to avoid nulls
demonstrateNullSafeFiltering();
}
public static boolean isShortWord(String word) {
return word.length() <= 5;
}
public static void demonstrateDynamicFiltering(List<String> words) {
System.out.println("\n=== Dynamic Filtering ===");
// Create filters dynamically based on criteria
Map<String, Predicate<String>> filters = new HashMap<>();
filters.put("length>5", s -> s.length() > 5);
filters.put("containsA", s -> s.contains("a"));
filters.put("endsWithE", s -> s.endsWith("e"));
// Apply multiple filters dynamically
Predicate<String> combinedFilter = filters.values().stream()
.reduce(Predicate::and)
.orElse(s -> true);
List<String> dynamicallyFiltered = words.stream()
.filter(combinedFilter)
.collect(Collectors.toList());
System.out.println("Dynamically filtered: " + dynamicallyFiltered);
// Filter based on user selection
String userChoice = "length>5";
Predicate<String> userFilter = filters.getOrDefault(userChoice, s -> true);
List<String> userFiltered = words.stream()
.filter(userFilter)
.collect(Collectors.toList());
System.out.println("User filtered (" + userChoice + "): " + userFiltered);
}
public static void demonstrateNullSafeFiltering() {
System.out.println("\n=== Null-Safe Filtering ===");
List<String> listWithNulls = Arrays.asList("apple", null, "banana", null, "cherry", "date");
// Filter out null values
List<String> withoutNulls = listWithNulls.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
System.out.println("Without nulls: " + withoutNulls);
// Filter nulls and empty strings
List<String> validStrings = listWithNulls.stream()
.filter(s -> s != null && !s.trim().isEmpty())
.collect(Collectors.toList());
System.out.println("Valid strings: " + validStrings);
// Complex objects with null checks
class Person {
String name;
Integer age;
Person(String name, Integer age) {
this.name = name;
this.age = age;
}
String getName() { return name; }
Integer getAge() { return age; }
}
List<Person> people = Arrays.asList(
new Person("John", 25),
new Person(null, 30),
new Person("Alice", null),
new Person("Bob", 35),
new Person(null, null)
);
List<Person> validPeople = people.stream()
.filter(p -> p.getName() != null && p.getAge() != null)
.collect(Collectors.toList());
System.out.println("Valid people: " +
validPeople.stream().map(p -> p.getName() + "(" + p.getAge() + ")").collect(Collectors.toList()));
}
}
2. map() Operation
Basic map() Usage
import java.util.*;
import java.util.stream.*;
public class MapOperation {
public static void main(String[] args) {
System.out.println("=== map() Operation ===");
List<String> names = Arrays.asList("John", "Alice", "Bob", "Carol", "David");
// 1. Convert to uppercase
List<String> upperCaseNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println("Uppercase: " + upperCaseNames);
// 2. Get name lengths
List<Integer> nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println("Name lengths: " + nameLengths);
// 3. Add prefix to names
List<String> prefixedNames = names.stream()
.map(name -> "Mr. " + name)
.collect(Collectors.toList());
System.out.println("Prefixed names: " + prefixedNames);
demonstrateNumberMapping();
demonstrateObjectMapping();
}
public static void demonstrateNumberMapping() {
System.out.println("\n=== Number Mapping ===");
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 1. Square numbers
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println("Squares: " + squares);
// 2. Convert to string with description
List<String> numberDescriptions = numbers.stream()
.map(n -> "Number: " + n + ", Square: " + (n * n))
.collect(Collectors.toList());
System.out.println("Number descriptions: " + numberDescriptions);
// 3. Mathematical transformations
List<Double> halfValues = numbers.stream()
.map(n -> n / 2.0)
.collect(Collectors.toList());
System.out.println("Half values: " + halfValues);
// 4. Chain multiple maps
List<String> complexTransform = numbers.stream()
.map(n -> n * 2) // Double the number
.map(n -> "Value: " + n) // Convert to string
.map(s -> s + "!") // Add exclamation
.collect(Collectors.toList());
System.out.println("Complex transform: " + complexTransform);
}
public static void demonstrateObjectMapping() {
System.out.println("\n=== Object Mapping ===");
class Person {
String firstName;
String lastName;
int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public int getAge() { return age; }
@Override
public String toString() {
return String.format("Person{firstName='%s', lastName='%s', age=%d}",
firstName, lastName, age);
}
}
List<Person> people = Arrays.asList(
new Person("John", "Doe", 25),
new Person("Alice", "Smith", 30),
new Person("Bob", "Johnson", 35),
new Person("Carol", "Williams", 28)
);
// 1. Extract first names only
List<String> firstNames = people.stream()
.map(Person::getFirstName)
.collect(Collectors.toList());
System.out.println("First names: " + firstNames);
// 2. Create full names
List<String> fullNames = people.stream()
.map(p -> p.getFirstName() + " " + p.getLastName())
.collect(Collectors.toList());
System.out.println("Full names: " + fullNames);
// 3. Transform to different object type
List<Map<String, Object>> personMaps = people.stream()
.map(p -> {
Map<String, Object> map = new HashMap<>();
map.put("name", p.getFirstName() + " " + p.getLastName());
map.put("age", p.getAge());
map.put("ageGroup", p.getAge() < 30 ? "Young" : "Adult");
return map;
})
.collect(Collectors.toList());
System.out.println("Person maps: " + personMaps);
// 4. Create formatted strings
List<String> formattedPeople = people.stream()
.map(p -> String.format("%s %s (%d years old)",
p.getFirstName(), p.getLastName(), p.getAge()))
.collect(Collectors.toList());
System.out.println("Formatted people: " + formattedPeople);
}
}
Advanced map() Techniques
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
public class AdvancedMapping {
public static void main(String[] args) {
System.out.println("=== Advanced Mapping Techniques ===");
// 1. Mapping with index
demonstrateIndexedMapping();
// 2. Conditional mapping
demonstrateConditionalMapping();
// 3. FlatMap vs Map
demonstrateFlatMapVsMap();
// 4. Mapping with state
demonstrateStatefulMapping();
}
public static void demonstrateIndexedMapping() {
System.out.println("\n=== Indexed Mapping ===");
List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry");
// Map with index using IntStream
List<String> indexedFruits = IntStream.range(0, fruits.size())
.mapToObj(i -> (i + 1) + ". " + fruits.get(i))
.collect(Collectors.toList());
System.out.println("Indexed fruits: " + indexedFruits);
// Alternative using custom collector (more complex but functional)
List<String> indexedFruits2 = fruits.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(),
list -> IntStream.range(0, list.size())
.mapToObj(i -> (i + 1) + ". " + list.get(i))
.collect(Collectors.toList())
));
System.out.println("Indexed fruits (alternative): " + indexedFruits2);
}
public static void demonstrateConditionalMapping() {
System.out.println("\n=== Conditional Mapping ===");
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Map even numbers to their square, odd numbers to negative
List<Integer> conditionalMap = numbers.stream()
.map(n -> n % 2 == 0 ? n * n : -n)
.collect(Collectors.toList());
System.out.println("Conditional mapping: " + conditionalMap);
// More complex conditional mapping
List<String> numberDescriptions = numbers.stream()
.map(n -> {
if (n % 2 == 0 && n % 3 == 0) {
return n + " is divisible by 6";
} else if (n % 2 == 0) {
return n + " is even";
} else if (n % 3 == 0) {
return n + " is divisible by 3";
} else {
return n + " is odd";
}
})
.collect(Collectors.toList());
System.out.println("Number descriptions: " + numberDescriptions);
}
public static void demonstrateFlatMapVsMap() {
System.out.println("\n=== FlatMap vs Map ===");
List<List<Integer>> listOfLists = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6),
Arrays.asList(7, 8, 9)
);
// Using map - returns Stream<List<Integer>>
List<List<Integer>> mappedResult = listOfLists.stream()
.map(list -> list.stream().map(n -> n * 2).collect(Collectors.toList()))
.collect(Collectors.toList());
System.out.println("Map result (list of lists): " + mappedResult);
// Using flatMap - returns Stream<Integer>
List<Integer> flatMappedResult = listOfLists.stream()
.flatMap(list -> list.stream().map(n -> n * 2))
.collect(Collectors.toList());
System.out.println("FlatMap result (flat list): " + flatMappedResult);
// Real-world example: extracting all words from sentences
List<String> sentences = Arrays.asList(
"Hello world",
"Java streams are powerful",
"Functional programming is fun"
);
List<String> allWords = sentences.stream()
.flatMap(sentence -> Arrays.stream(sentence.split(" ")))
.collect(Collectors.toList());
System.out.println("All words: " + allWords);
}
public static void demonstrateStatefulMapping() {
System.out.println("\n=== Stateful Mapping ===");
// Note: This is generally not recommended as it's not thread-safe
// and goes against functional programming principles
List<String> items = Arrays.asList("A", "B", "C", "D", "E");
// Stateful mapping using AtomicInteger (thread-safe but not pure functional)
AtomicInteger counter = new AtomicInteger(1);
List<String> numberedItems = items.stream()
.map(item -> counter.getAndIncrement() + ". " + item)
.collect(Collectors.toList());
System.out.println("Numbered items: " + numberedItems);
// Better approach: use zip with index (if available) or other techniques
List<String> betterNumbered = IntStream.range(0, items.size())
.mapToObj(i -> (i + 1) + ". " + items.get(i))
.collect(Collectors.toList());
System.out.println("Better numbered: " + betterNumbered);
}
}
3. Combining filter() and map()
filter() and map() Together
import java.util.*;
import java.util.stream.*;
public class FilterAndMapCombination {
static class Employee {
String name;
String department;
double salary;
int experience;
public Employee(String name, String department, double salary, int experience) {
this.name = name;
this.department = department;
this.salary = salary;
this.experience = experience;
}
public String getName() { return name; }
public String getDepartment() { return department; }
public double getSalary() { return salary; }
public int getExperience() { return experience; }
@Override
public String toString() {
return String.format("Employee{name='%-10s', dept='%-12s', salary=$%-8.2f, exp=%d}",
name, department, salary, experience);
}
}
public static void main(String[] args) {
System.out.println("=== Combining filter() and map() ===");
List<Employee> employees = Arrays.asList(
new Employee("John", "Engineering", 75000, 3),
new Employee("Alice", "Engineering", 95000, 5),
new Employee("Bob", "Marketing", 65000, 2),
new Employee("Carol", "Engineering", 85000, 4),
new Employee("David", "Marketing", 70000, 6),
new Employee("Eve", "HR", 55000, 1),
new Employee("Frank", "Engineering", 110000, 8),
new Employee("Grace", "HR", 60000, 3)
);
// 1. Filter and map in sequence
System.out.println("=== Engineering employees with formatted info ===");
List<String> engineeringInfo = employees.stream()
.filter(e -> "Engineering".equals(e.getDepartment())) // Filter engineering
.map(e -> String.format("%s - $%.2f (%d years)", // Map to formatted string
e.getName(), e.getSalary(), e.getExperience()))
.collect(Collectors.toList());
engineeringInfo.forEach(System.out::println);
// 2. Multiple filters then map
System.out.println("\n=== Senior Engineering employees ===");
List<String> seniorEngineers = employees.stream()
.filter(e -> "Engineering".equals(e.getDepartment()))
.filter(e -> e.getExperience() >= 5)
.filter(e -> e.getSalary() > 90000)
.map(Employee::getName)
.collect(Collectors.toList());
System.out.println("Senior engineers: " + seniorEngineers);
// 3. Filter then complex mapping
System.out.println("\n=== Employee salary analysis ===");
List<String> salaryAnalysis = employees.stream()
.filter(e -> e.getSalary() > 70000)
.map(e -> {
double bonus = e.getSalary() * 0.1;
double totalComp = e.getSalary() + bonus;
return String.format("%s: Salary=$%.2f, Bonus=$%.2f, Total=$%.2f",
e.getName(), e.getSalary(), bonus, totalComp);
})
.collect(Collectors.toList());
salaryAnalysis.forEach(System.out::println);
// 4. Department-wise processing
System.out.println("\n=== Department summary ===");
Map<String, List<String>> deptSummary = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.mapping(
e -> String.format("%s ($%.2f)", e.getName(), e.getSalary()),
Collectors.toList()
)
));
deptSummary.forEach((dept, empList) -> {
System.out.println(dept + ": " + empList);
});
demonstratePerformanceConsiderations();
}
public static void demonstratePerformanceConsiderations() {
System.out.println("\n=== Performance Considerations ===");
List<Integer> numbers = IntStream.range(1, 1000000).boxed().collect(Collectors.toList());
// Less efficient: multiple intermediate operations
long startTime = System.currentTimeMillis();
List<Integer> result1 = numbers.stream()
.filter(n -> n % 2 == 0) // Filter first
.map(n -> n * 2) // Then map
.filter(n -> n > 1000) // Then filter again
.collect(Collectors.toList());
long time1 = System.currentTimeMillis() - startTime;
// More efficient: combine conditions when possible
startTime = System.currentTimeMillis();
List<Integer> result2 = numbers.stream()
.filter(n -> n % 2 == 0 && n * 2 > 1000) // Combined filter
.map(n -> n * 2) // Single map
.collect(Collectors.toList());
long time2 = System.currentTimeMillis() - startTime;
System.out.printf("Multiple operations time: %d ms%n", time1);
System.out.printf("Combined operations time: %d ms%n", time2);
System.out.println("Results are equal: " + result1.equals(result2));
// Order matters for performance
List<String> mixedList = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
// Inefficient order: expensive operation before filter
List<Integer> inefficient = mixedList.stream()
.map(s -> {
// Simulate expensive operation
try { Thread.sleep(1); } catch (InterruptedException e) {}
return Integer.parseInt(s);
})
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// Efficient order: filter before expensive operation
List<Integer> efficient = mixedList.stream()
.filter(s -> Integer.parseInt(s) % 2 == 0) // Cheap filter first
.map(s -> {
// Expensive operation only on filtered elements
try { Thread.sleep(1); } catch (InterruptedException e) {}
return Integer.parseInt(s);
})
.collect(Collectors.toList());
System.out.println("Inefficient count: " + inefficient.size());
System.out.println("Efficient count: " + efficient.size());
}
}
Real-World Examples with filter() and map()
import java.util.*;
import java.util.stream.*;
public class RealWorldFilterMapExamples {
static class Order {
String orderId;
String customerName;
double amount;
String status;
List<String> items;
public Order(String orderId, String customerName, double amount, String status, List<String> items) {
this.orderId = orderId;
this.customerName = customerName;
this.amount = amount;
this.status = status;
this.items = items;
}
public String getOrderId() { return orderId; }
public String getCustomerName() { return customerName; }
public double getAmount() { return amount; }
public String getStatus() { return status; }
public List<String> getItems() { return items; }
@Override
public String toString() {
return String.format("Order{id='%s', customer='%s', amount=$%.2f, status='%s', items=%s}",
orderId, customerName, amount, status, items);
}
}
public static void main(String[] args) {
System.out.println("=== Real-World Examples: E-Commerce System ===");
List<Order> orders = Arrays.asList(
new Order("ORD001", "John Doe", 150.50, "DELIVERED",
Arrays.asList("Laptop", "Mouse")),
new Order("ORD002", "Alice Smith", 75.25, "PENDING",
Arrays.asList("Book", "Pen")),
new Order("ORD003", "Bob Johnson", 300.75, "DELIVERED",
Arrays.asList("Smartphone", "Case")),
new Order("ORD004", "Carol Williams", 45.99, "CANCELLED",
Arrays.asList("T-Shirt")),
new Order("ORD005", "David Brown", 200.00, "DELIVERED",
Arrays.asList("Headphones", "Adapter")),
new Order("ORD006", "Eve Davis", 89.99, "PENDING",
Arrays.asList("Shoes", "Socks"))
);
// 1. Get delivered orders with customer names
System.out.println("=== Delivered Orders ===");
List<String> deliveredOrders = orders.stream()
.filter(order -> "DELIVERED".equals(order.getStatus()))
.map(order -> order.getCustomerName() + " - Order: " + order.getOrderId())
.collect(Collectors.toList());
deliveredOrders.forEach(System.out::println);
// 2. High-value pending orders
System.out.println("\n=== High-Value Pending Orders ===");
List<String> highValuePending = orders.stream()
.filter(order -> "PENDING".equals(order.getStatus()))
.filter(order -> order.getAmount() > 50.0)
.map(order -> String.format("%s: $%.2f - %s",
order.getOrderId(), order.getAmount(),
String.join(", ", order.getItems())))
.collect(Collectors.toList());
highValuePending.forEach(System.out::println);
// 3. Extract all unique items from all orders
System.out.println("\n=== All Unique Items Ordered ===");
Set<String> allItems = orders.stream()
.flatMap(order -> order.getItems().stream()) // Flatten all items
.map(String::toLowerCase) // Normalize case
.collect(Collectors.toSet()); // Remove duplicates
System.out.println("All items: " + allItems);
// 4. Order summary by status
System.out.println("\n=== Order Summary by Status ===");
Map<String, List<String>> ordersByStatus = orders.stream()
.collect(Collectors.groupingBy(
Order::getStatus,
Collectors.mapping(
order -> String.format("%s ($%.2f)", order.getOrderId(), order.getAmount()),
Collectors.toList()
)
));
ordersByStatus.forEach((status, orderList) -> {
System.out.println(status + ": " + orderList);
});
// 5. Customer spending analysis
System.out.println("\n=== Customer Spending Analysis ===");
Map<String, Double> customerSpending = orders.stream()
.filter(order -> !"CANCELLED".equals(order.getStatus()))
.collect(Collectors.groupingBy(
Order::getCustomerName,
Collectors.summingDouble(Order::getAmount)
));
customerSpending.forEach((customer, total) -> {
System.out.printf("%-15s: $%.2f%n", customer, total);
});
demonstrateDataTransformationPipeline();
}
public static void demonstrateDataTransformationPipeline() {
System.out.println("\n=== Data Transformation Pipeline ===");
class Product {
String id;
String name;
double price;
int rating;
public Product(String id, String name, double price, int rating) {
this.id = id;
this.name = name;
this.price = price;
this.rating = rating;
}
public String getId() { return id; }
public String getName() { return name; }
public double getPrice() { return price; }
public int getRating() { return rating; }
}
List<Product> products = Arrays.asList(
new Product("P001", "Laptop", 999.99, 4),
new Product("P002", "Mouse", 25.99, 3),
new Product("P003", "Keyboard", 75.50, 5),
new Product("P004", "Monitor", 299.99, 4),
new Product("P005", "Headphones", 149.99, 2),
new Product("P006", "Webcam", 89.99, 4)
);
// Complete data processing pipeline
List<String> recommendedProducts = products.stream()
// Filter: Only highly rated products (4+ stars)
.filter(p -> p.getRating() >= 4)
// Filter: Affordable products (under $300)
.filter(p -> p.getPrice() < 300.0)
// Map: Apply 10% discount
.map(p -> new Product(p.getId(), p.getName(), p.getPrice() * 0.9, p.getRating()))
// Map: Create recommendation string
.map(p -> String.format("💡 %s - Now $%.2f! (Rating: %d/5)",
p.getName(), p.getPrice(), p.getRating()))
// Sort by price (ascending)
.sorted()
// Collect results
.collect(Collectors.toList());
System.out.println("Recommended Products:");
recommendedProducts.forEach(System.out::println);
// Statistical analysis
DoubleSummaryStatistics stats = products.stream()
.filter(p -> p.getRating() >= 4)
.mapToDouble(Product::getPrice)
.summaryStatistics();
System.out.printf("\nPrice Statistics for Highly Rated Products:%n");
System.out.printf("Count: %d, Average: $%.2f, Min: $%.2f, Max: $%.2f%n",
stats.getCount(), stats.getAverage(),
stats.getMin(), stats.getMax());
}
}
Summary
filter() Operation:
- Purpose: Selects elements based on a predicate
- Syntax:
.filter(Predicate<T> predicate) - Returns: Stream containing only elements that match the predicate
- Use Cases: Data filtering, validation, conditional selection
map() Operation:
- Purpose: Transforms each element to another type
- Syntax:
.map(Function<T, R> mapper) - Returns: Stream of transformed elements
- Use Cases: Data transformation, extraction, formatting
Key Points:
- Lazy Evaluation: Intermediate operations are only executed when terminal operation is called
- Method Chaining: Multiple intermediate operations can be chained
- Order Matters: Performance can be affected by operation order
- Immutability: Original data source is not modified
- Thread Safety: Stream operations are generally thread-safe
Best Practices:
- Filter early: Apply filters before expensive operations
- Combine conditions: Use single filter with combined conditions when possible
- Use method references: For better readability
- Avoid stateful operations: In mapping functions for purity
- Consider performance: For large datasets, test different operation orders
These intermediate operations form the foundation of functional data processing in Java, enabling clean, readable, and efficient data transformation pipelines.