Ace
Ace

Reputation: 869

Find the earliest date among 3 dates

I have three dates in Java: a, b, c. Any or all of these dates may be null. What's the most efficient way of determining the earliest date among a,b,c, without having a massive if-else block? I would want a the actual date that is earliest among a,b,c. Any date is better than a null date.

Upvotes: 28

Views: 58241

Answers (15)

Anonymous
Anonymous

Reputation: 86223

Arrays.asList(), Collections.min() and a null-safe comparator

    LocalDate a = LocalDate.of(2025, Month.JANUARY, 6);
    LocalDate b = null;
    LocalDate c = LocalDate.of(2023, Month.NOVEMBER, 22);

    List<LocalDate> dates = Arrays.asList(a, b, c);
    LocalDate earliest = 
        Collections.min(
            dates,
            Comparator.nullsLast(Comparator.naturalOrder())
        );

    System.out.println("Earliest date: " + earliest);

I consider this both clear and short and simple, also compared to the stream solutions in a couple of other answers. Output is:

Earliest date: 2023-11-22

If all three dates happen to be null, the output is:

Earliest date: null

Often we prefer List.of() over Arrays.asList() when passing each element; but since List.of() does not accepts nulls, we can’t use it here.

Comparators are often used with sorting, but they are also very practical for just finding the minimum or maximum. My code does not perform any complete sort, it only finds the min according to the comparator.

Do use LocalDate from java.time, the modern Java date and time API, for a date. The Date class used in some of the answers had severe design problems and has been outdated the last 10 years (and counting).

The idea can obviously be used with any class that implements Comparable and any number of objects of that class.

Documentation links

Upvotes: 6

SKO
SKO

Reputation: 50

If you're also using kotlin and if entries are not null, it becomes as simple as minOf(a, b, c). If they can be null, listOfNotNull(a, b, c).minOrNull().
For a collection, it's dates.filterNotNull().minOrNull().

Upvotes: 0

yglodt
yglodt

Reputation: 14551

java.time.LocalDate

The java.util.Date & java.sql.Date classes were supplanted by the java.time classes defined in JSR 310.

For Java 8 and later, use LocalDate for a date-only value.

To get the earliest of a list of dates:

import java.time.LocalDate;

public static LocalDate earliestDate(LocalDate... dates) {
    return
        Arrays
        .stream(dates)
        .filter(Objects::nonNull)
        .min(LocalDate::compareTo)
        .orElse(null);
}

Upvotes: 9

Maksim Sirotkin
Maksim Sirotkin

Reputation: 473

List.of(date1, date2)
                .stream()
                .min(Date::compareTo)
                .get();

It is simple solution which I wanted to share that could be used in a private method when you are sure all dates are not null.

Upvotes: -1

n0nick
n0nick

Reputation: 1145

When Apache Commons is available, you might use ObjectUtils.min:

Date earliest = ObjectUtils.min(a, b, c);

Upvotes: 13

michal.jakubeczy
michal.jakubeczy

Reputation: 9469

Using stream:

Date min = Stream.of(date1, date2, etc)
  .filter(Objects::nonNull)
  .min(Date::compareTo)
  .orElse(null);

If your array does not contain NULL values you can use Ordering

Ordering.natural().min(date1, date2, etc);

Upvotes: 2

pavelety
pavelety

Reputation: 874

Java 8+ oneliner. To make it safe, null check is added. Pass any number of dates.

public static Date min(Date... dates) {
    return Arrays.stream(dates).filter(Objects::nonNull).min(Date::compareTo).orElse(null);
}

Not null safe, but much shorter:

public static Date min(Date... dates) {
    return Collections.min(Arrays.asList(dates));
}

Not null safe without a new method:

Collections.min(Arrays.asList(date1, date2));

Upvotes: 24

Grzegorz Olszewski
Grzegorz Olszewski

Reputation: 1418

Well, 'efficient' has some different meanings, but I don't think there will be an efficiency problem with comparing three dates. In fact, it's really cheap. You can try this approach:

SortedSet<Date> dates = new TreeSet<Date>();
dates.add(date1);
dates.add(date2);
// ...
dates.add(dateN);
Date earliest = dates.first();

Or, maybe more elegant:

for (Date date : someDates) {
   if (date != null) {
      dates.add(date);
   }
}
Date earliest = dates.first();

Upvotes: 8

Danny
Danny

Reputation: 7518

Some Java 8 methods using streams. The first will filter nulls before comparing, the second will put them at the end of the list.

Date minDate = Arrays.asList(date1, date2, etc).stream()
     .filter(Objects::nonNull).min(Date::compareTo).get()

or

Date minDate = Arrays.asList(date1, date2, etc).stream()
    .sorted((a, b) -> {
        //some kind of custom sort.
        if(a == null && b == null) return 0;
        if(a == null) return 1;
        if(b == null) return -1;
        return a.compareTo(b);
    }).findFirst().get()

Upvotes: 6

Justinas Jakavonis
Justinas Jakavonis

Reputation: 8798

Another way is to use java.util.Collections.min(collection):

Returns: the minimum element of the given collection, according to the natural ordering of its elements.

public static Date getEarliestDate(List<Date> dates) {

    if (dates == null || dates.isEmpty())
        return null;

    dates.removeIf(Objects::isNull);

    return dates.isEmpty() ? null : Collections.min(dates);
}

Upvotes: 2

Mortada Jafar
Mortada Jafar

Reputation: 3679

using before and after :

  /**
     * find Min Dates
     * @param date1
     * @param date2
     * @return
     */
    public static Date minDate(Date date1, Date date2) {
        // if date1 before date2 then return date1 else return date2
        return date1.before(date2) ? date1 : date2;
    }
     /**
     * find Max Dates
     * @param date1
     * @param date2
     * @return
     */
    public static Date maxDate(Date date1, Date date2) {
        // if date1 after date2 then return date1 else return date2
        return date1.after(date2) ? date1 : date2;
    }

Upvotes: 2

Bohemian
Bohemian

Reputation: 424983

There's no getting around null checking, but with some refactoring you can make it painless.

Create a method that safely compares two dates:

/**
 * Safely compare two dates, null being considered "greater" than a Date
 * @return the earliest of the two
 */
public static Date least(Date a, Date b) {
    return a == null ? b : (b == null ? a : (a.before(b) ? a : b));
}

then combine calls to that:

Date earliest = least(least(a, b), c);

Actually, you can make this a generic method for any Comparable:

public static <T extends Comparable<T>> T least(T a, T b) {
    return a == null ? b : (b == null ? a : (a.compareTo(b) < 0 ? a : b));
}

Upvotes: 35

Vitaly
Vitaly

Reputation: 2790

just another version as an idea and for fun

new Date(Math.min(a != null ? a.getTime() : Long.MAX_VALUE
, Math.min(b != null ? b.getTime() : Long.MAX_VALUE
, c != null ? c.getTime() : Long.MAX_VALUE)))

Upvotes: 0

NullPointerException
NullPointerException

Reputation: 3804

You can use

date1.compareTo(anotherDate)

Returns:

the value 0 if the argument Date is equal to this Date; a value less than 0 if this Date is before the Date argument; and a value greater than 0 if this Date is after the Date argument.

Throws:

NullPointerException - 

if anotherDate is null.

Upvotes: 0

Cristiano
Cristiano

Reputation: 2909

Use the java Date object http://docs.oracle.com/javase/6/docs/api/java/util/Date.html

You can use the before() and after() functions of these objects then

Upvotes: 2

Related Questions