0

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();
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
acG
  • 97
  • 2
  • 6

2 Answers2

3

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);

It happens to be the same as you've written, and since your original question was how to create a comparator based on more than one attribute, here is another example:

public static final Comparator<Employee> BY_NAME_DATE =
    Comparator.comparing(Employee::getName)
        .thenComparing(Employee::getDate);

You can declare comparators for a class that has no natural order right inside this class as public static field and use whenever you need them, instead of creating a new comparator on the spot each time.

With Java 8 a lot of handy static methods were introduced in the Comparator interface. You can chain these methods in a fluent way because they all give you a new comparator. To combine two comparators, you can use methods like thenComparing() and thenComparingInt(). To learn how to build comparators using Java-8 methods, have a look at this tutorial.

Note that there could be some intricate cases related to how the type inference works in Java, so I'm also suggesting reading this question.

I'm not sure if duplicates dates can be a problem

There would be no problem with employees having identical latest date-time information associated with them, since you want to get the first encountered element. The problem can occur if there's no data in the stream at all.

That leads to the second suggestion - 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(byDate)
    .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().

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
2

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.

Magnilex
  • 11,584
  • 9
  • 62
  • 84