如何用Java 8 Stream按时间戳排序含Address列表的Employee对象?
Got it, let's break down how to solve this with Java 8 Streams, making sure we handle both cases where an Employee has an Address list and where that list is null or empty.
First, let's clarify the sorting logic (since you didn't specify exactly which timestamp to use): I'll assume we want to sort employees based on their most recent residence start date (startDate in Address). Employees with no addresses (null/empty list) will be placed at the end of the sorted list—we can adjust this later if you want them at the front.
Step-by-Step Implementation
Extract the key timestamp for each Employee
We need a helper function that takes anEmployeeand returns the relevant timestamp, handling null/empty address lists safely to avoidNullPointerException:// Helper to get the latest startDate for an Employee, or a default if no addresses private static Timestamp getLatestStartDate(Employee employee) { return Optional.ofNullable(employee.getAddresses()) .orElse(Collections.emptyList()) .stream() .map(Address::getStartDate) .filter(Objects::nonNull) // Skip addresses with null startDate .max(Timestamp::compareTo) // Get the most recent start date .orElse(Timestamp.from(Instant.EPOCH)); // Default to earliest possible time if no valid dates }Sort the Employee Stream
UseStream.sorted()with a comparator that leverages our helper function. Here's how to sort a list of employees:List<Employee> employees = // Your input list of employees List<Employee> sortedEmployees = employees.stream() .sorted(Comparator.comparing( EmployeeSorter::getLatestStartDate, // Use our helper to get the sort key Comparator.reverseOrder() // Sort from most recent to oldest (remove for ascending) )) .collect(Collectors.toList());
Handling Edge Cases
- Null Address lists:
Optional.ofNullable(employee.getAddresses()).orElse(Collections.emptyList())ensures we never try to stream a null list. - Addresses with null timestamps: The
filter(Objects::nonNull)line skips anyAddressentries that don't have a validstartDate. - Change priority for no-address employees: If you want employees with no addresses to come first, replace
Instant.EPOCHwithInstant.MAXin the default timestamp.
Alternative: Sort by End Date
If you want to sort by the latest end date instead, just adjust the helper function to use Address::getEndDate:
private static Timestamp getLatestEndDate(Employee employee) { return Optional.ofNullable(employee.getAddresses()) .orElse(Collections.emptyList()) .stream() .map(Address::getEndDate) .filter(Objects::nonNull) .max(Timestamp::compareTo) .orElse(Timestamp.from(Instant.EPOCH)); }
Full Example Code
Here's a complete, runnable snippet to tie it all together:
import java.sql.Timestamp; import java.time.Instant; import java.util.*; import java.util.stream.Collectors; class Employee { private String name; private List<Address> addresses; // Getters and Setters public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Address> getAddresses() { return addresses; } public void setAddresses(List<Address> addresses) { this.addresses = addresses; } } class Address { private String city; private Timestamp startDate; private Timestamp endDate; private String typeOfResidence; // Getters and Setters public Timestamp getStartDate() { return startDate; } public void setStartDate(Timestamp startDate) { this.startDate = startDate; } } public class EmployeeSorter { public static void main(String[] args) { // Test data Employee emp1 = new Employee(); emp1.setName("Alice"); List<Address> emp1Addresses = new ArrayList<>(); emp1Addresses.add(createAddress(Timestamp.from(Instant.parse("2022-01-01T00:00:00Z")))); emp1.setAddresses(emp1Addresses); Employee emp2 = new Employee(); emp2.setName("Bob"); emp2.setAddresses(null); // No addresses Employee emp3 = new Employee(); emp3.setName("Charlie"); List<Address> emp3Addresses = new ArrayList<>(); emp3Addresses.add(createAddress(Timestamp.from(Instant.parse("2023-05-01T00:00:00Z")))); emp3.setAddresses(emp3Addresses); List<Employee> employees = Arrays.asList(emp1, emp2, emp3); // Sort employees by latest start date, no-address employees at end List<Employee> sortedEmployees = employees.stream() .sorted(Comparator.comparing(EmployeeSorter::getLatestStartDate, Comparator.reverseOrder())) .collect(Collectors.toList()); // Print results sortedEmployees.forEach(emp -> System.out.println(emp.getName() + " - Latest Start Date: " + getLatestStartDate(emp))); } private static Timestamp getLatestStartDate(Employee employee) { return Optional.ofNullable(employee.getAddresses()) .orElse(Collections.emptyList()) .stream() .map(Address::getStartDate) .filter(Objects::nonNull) .max(Timestamp::compareTo) .orElse(Timestamp.from(Instant.EPOCH)); } private static Address createAddress(Timestamp startDate) { Address addr = new Address(); addr.setStartDate(startDate); return addr; } }
This will output:
Charlie - Latest Start Date: 2023-05-01 00:00:00.0 Alice - Latest Start Date: 2022-01-01 00:00:00.0 Bob - Latest Start Date: 1970-01-01 00:00:00.0
内容的提问来源于stack exchange,提问作者azaveri7

