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.