Index

Real-world Data Processing Examples

Java Streams

17.1 Stream-Based Data Filtering and Reporting

Streams provide a powerful, declarative way to filter large datasets and generate insightful reports. By chaining operations like filter(), map(), collect(), and aggregation methods, you can transform raw data into meaningful summaries efficiently and readably.

Below are two practical examples from common domains: employee performance filtering and product expiration reporting.

Example 1: Filtering Top-Performing Employees

Suppose you have a list of employees with sales numbers and want to generate a report of employees who exceeded a sales threshold.

import java.util.*;
import java.util.stream.Collectors;

class Employee {
    String name;
    double sales;

    Employee(String name, double sales) {
        this.name = name;
        this.sales = sales;
    }

    public double getSales() {
        return sales;
    }

    @Override
    public String toString() {
        return name + " (Sales: " + sales + ")";
    }
}

public class EmployeeReport {
    public static void main(String[] args) {
        List<Employee> employees = List.of(
            new Employee("Alice", 15000),
            new Employee("Bob", 9000),
            new Employee("Carol", 20000),
            new Employee("David", 12000),
            new Employee("Eve", 8000)
        );

        double threshold = 10000;

        List<Employee> topPerformers = employees.stream()
            .filter(e -> e.getSales() > threshold)
            .sorted(Comparator.comparingDouble(Employee::getSales).reversed())
            .collect(Collectors.toList());

        System.out.println("Top Performing Employees:");
        topPerformers.forEach(System.out::println);
    }
}

Output:

Top Performing Employees:
Carol (Sales: 20000.0)
Alice (Sales: 15000.0)
David (Sales: 12000.0)

Explanation: We start by filtering employees whose sales exceed the threshold, then sort them in descending order by sales, and finally collect them into a list for reporting.

Example 2: Extracting All Expired Products from a Catalog

Assume you manage a product catalog with expiration dates. The goal is to extract and report all products that have expired as of today.

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

class Product {
    String name;
    LocalDate expirationDate;

    Product(String name, LocalDate expirationDate) {
        this.name = name;
        this.expirationDate = expirationDate;
    }

    @Override
    public String toString() {
        return name + " (Expires: " + expirationDate + ")";
    }
}

public class ProductReport {
    public static void main(String[] args) {
        List<Product> products = List.of(
            new Product("Milk", LocalDate.now().minusDays(1)),
            new Product("Bread", LocalDate.now().plusDays(2)),
            new Product("Cheese", LocalDate.now().minusDays(5)),
            new Product("Butter", LocalDate.now().plusDays(10))
        );

        LocalDate today = LocalDate.now();

        List<Product> expiredProducts = products.stream()
            .filter(p -> p.expirationDate.isBefore(today) || p.expirationDate.isEqual(today))
            .collect(Collectors.toList());

        System.out.println("Expired Products:");
        expiredProducts.forEach(System.out::println);
    }
}

Output:

Expired Products:
Milk (Expires: 2025-06-22)
Cheese (Expires: 2025-06-18)

Explanation: The stream filters products whose expiration date is before or equal to the current date, gathering all expired items for the report.

Summary

These patterns apply broadly—from HR analytics to inventory management—helping transform large data into actionable insights.

Index

17.2 Transforming and Exporting Data

Streams are ideal for reshaping and preparing data for export into formats such as JSON, CSV, or custom string representations. By combining mapping operations and collectors, you can easily transform domain objects into Data Transfer Objects (DTOs) or formatted strings, and then write them to files or other output streams.

Example 1: Mapping Domain Objects to CSV Format

Suppose you have a list of Employee objects and want to export their details as CSV lines.

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;

class Employee {
    String name;
    String department;
    double salary;

    Employee(String name, String department, double salary) {
        this.name = name;
        this.department = department;
        this.salary = salary;
    }
}

public class CsvExportExample {
    public static void main(String[] args) throws IOException {
        List<Employee> employees = List.of(
            new Employee("Alice", "Engineering", 85000),
            new Employee("Bob", "Sales", 72000),
            new Employee("Carol", "Engineering", 91000)
        );

        // Map each employee to a CSV line
        List<String> csvLines = employees.stream()
            .map(e -> String.join(",", e.name, e.department, String.valueOf(e.salary)))
            .collect(Collectors.toList());

        // Add header at the beginning
        csvLines.add(0, "Name,Department,Salary");

        // Write to CSV file
        Path outputPath = Path.of("employees.csv");
        Files.write(outputPath, csvLines);

        System.out.println("CSV export completed: " + outputPath.toAbsolutePath());
    }
}

Explanation: We transform each Employee into a CSV string using map(), collect the lines into a list, prepend a header line, and write the list to a file with Files.write(). This approach preserves the data structure and is easy to maintain or extend.

Example 2: Exporting Data as JSON-like Strings

For lightweight JSON export without external libraries, streams can build JSON representations manually.

import java.util.List;
import java.util.stream.Collectors;

class Product {
    String name;
    double price;

    Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
}

public class JsonExportExample {
    public static void main(String[] args) {
        List<Product> products = List.of(
            new Product("Laptop", 1200.50),
            new Product("Phone", 650.00),
            new Product("Tablet", 300.99)
        );

        String json = products.stream()
            .map(p -> String.format("{\"name\":\"%s\",\"price\":%.2f}", p.name, p.price))
            .collect(Collectors.joining(", ", "[", "]"));

        System.out.println("JSON output:");
        System.out.println(json);
    }
}

Explanation: This example transforms each Product into a JSON-like string, then joins them with commas, wrapping the entire collection in square brackets to form a JSON array. This pattern is useful for simple JSON export without external dependencies.

Key Points

Streams provide a clean, functional approach to transform and export data efficiently, supporting formats ranging from CSV and JSON to any custom output your application requires.

Index

17.3 Stream Processing in Web Applications

In modern web applications, Java Streams offer a powerful, concise way to handle common data processing tasks such as filtering API request payloads, transforming query results, and preparing data for JSON responses. Their fluent, declarative style fits naturally within service layers or controllers, enabling clean, readable, and maintainable code.

Example 1: Filtering User-submitted Form Data

Imagine a REST endpoint receives a list of user roles submitted from a form. The service needs to validate and filter out invalid or duplicate roles before further processing.

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

class UserRoleService {
    private static final Set<String> VALID_ROLES = Set.of("ADMIN", "USER", "MODERATOR");

    public List<String> filterValidRoles(List<String> submittedRoles) {
        return submittedRoles.stream()
            .map(String::toUpperCase)                 // Normalize case
            .filter(VALID_ROLES::contains)            // Keep only valid roles
            .distinct()                              // Remove duplicates
            .collect(Collectors.toList());
    }
}

// Example usage in a controller or service
public class UserController {
    public static void main(String[] args) {
        UserRoleService service = new UserRoleService();

        List<String> submittedRoles = List.of("admin", "user", "guest", "user");
        List<String> filteredRoles = service.filterValidRoles(submittedRoles);

        System.out.println("Filtered Roles: " + filteredRoles);
    }
}

Explanation: This example uses a stream pipeline to clean and validate input, demonstrating how Streams can be integrated into business logic for form processing or API input validation.

Example 2: Transforming Database Query Results for JSON Response

Suppose your backend retrieves a list of Product entities from the database, but the API response requires a simplified DTO with only name and price, formatted as strings.

import java.util.List;
import java.util.stream.Collectors;

class Product {
    String name;
    double price;
    String description; // extra field not needed in response

    Product(String name, double price, String description) {
        this.name = name;
        this.price = price;
        this.description = description;
    }
}

class ProductDTO {
    String name;
    String price;

    ProductDTO(String name, String price) {
        this.name = name;
        this.price = price;
    }
}

class ProductService {
    public List<ProductDTO> getProductDTOs(List<Product> products) {
        return products.stream()
            .map(p -> new ProductDTO(p.name, String.format("$%.2f", p.price)))
            .collect(Collectors.toList());
    }
}

// Simulated Controller method
public class ProductController {
    public static void main(String[] args) {
        List<Product> products = List.of(
            new Product("Laptop", 1200.99, "High-end laptop"),
            new Product("Mouse", 25.50, "Wireless mouse"),
            new Product("Keyboard", 45.00, "Mechanical keyboard")
        );

        ProductService productService = new ProductService();
        List<ProductDTO> response = productService.getProductDTOs(products);

        // In a real app, response would be serialized as JSON by the web framework
        response.forEach(dto -> System.out.println(dto.name + ": " + dto.price));
    }
}

Explanation: Here, streams cleanly handle the conversion of entity objects to DTOs tailored for API responses, demonstrating separation of concerns and data shaping before JSON serialization.

Why Use Streams in Web Apps?

Best Practices

Streams empower Java web applications to efficiently process, validate, and transform data, enhancing code maintainability and performance in APIs and user interfaces alike.

Index