Home > Back-end >  Java Stream: Find the Max element based on Two properties
Java Stream: Find the Max element based on Two properties

Time:07-08

I have a List of objects that have this structure

List<Employee> empList = new ArrayList<>();

empList.add(new Employee(1, "2022-07-07", "15:31"));
empList.add(new Employee(2, "2022-07-07", "12:15"));
empList.add(new Employee(3, "2021-04-05", "20:55"));
empList.add(new Employee(4, "2021-03-05", "17:14"));

Is it possible to use Stream API to find the object with the latest date and time? In this case, I would get the first object.

I know that it is possible to do this, but in this case I'm only filtering by date, and I'm not sure if duplicates dates can be a problem.

Comparator<Employee> employeeComparator = 
    Comparator.comparing(Employee::getDate);
 
Employee emp = getEmployeeList().stream()
    .max(employeeComparator)
    .get();

CodePudding user response:

You can simply add thenComparing() to your comparator to compare by both date and time:

Comparator<Employee> employeeComparator = Comparator
    .comparing(Employee::getDate)
    .thenComparing(Employee::getTime);

Kind of self-explanatory, it would sort by date and then by time if two dates are identical.

You are correct that if you omit this, you shouldn't make any assumptions on which of the two elements having "2022-07-07" you would get.

CodePudding user response:

Never use strings to represent date-time information. Instead, use Java 8 classes from java.time package like LocalDateTime.

Assuming that Employee is reimplemented like that:

public class Employee {
    private String name;
    private LocalDateTime date;
    
    // constructor, getters, etc.
}

The comparator would be:

Comparator<Employee> byDate = Comparator.comparing(Employee::getDate);

To learn how to build comparators using Java-8 method have look at this tutorial.

The second thing - avoid invoking get() on the optional object, unless you didn't check the presence of the result (which in many cases is inconvenient and if you're finding yourself performing this check that might indicate that you're not leveraging Optional API to its full power).

If you have a look at the documentation of Java 10 and above, you'll see the following API note in the description of get():

The preferred alternative to this method is orElseThrow().

Method get() will produce NoSuchElementException in case of the empty optional, if you're OK with such outcome then apply orElseThrow() instead, it acts in the same way and makes your intention obvious to the reader of the code.

This method also has an overloaded version which allows you to specify the type of exception to throw.

Employee emp = getEmployeeList().stream()
    .max(employeeComparator)
    .orElseThrow();

You're not limited to using the methods mentioned above, Optional offers you other possibilities, for instance, you can provide a default value using orElse().

  • Related