找出3个日期中最早的日期

26

我在Java中有三个日期:a,b,c。这些日期中的任何一个或所有日期都可能为null。最有效的方法是什么以确定a、b、c中最早的日期,而不需要一个庞大的if-else块?


5
你尝试过哪些并不那么高效的方法? - asgs
你需要知道哪个日期是最早的(即将其与变量联系起来),还是只需要值? - Jon Skeet
3
你可以将每个日期转换为其毫秒表示,使用复合的 Math.min 函数确定最小值。 - MadProgrammer
4
空值(null)既不大于也不小于任何日期。 - Andre
我想要在a、b、c三个日期中最早的实际日期。任何日期都比空日期好。 - Ace
请参阅类似的问题,有没有不错的通用方法可以将空值排序到底部? - Basil Bourque
13个回答

34

无法避免空值检查,但通过一些重构可以使其变得轻松。

创建一个安全比较两个日期的方法:

/**
 * 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));
}

然后将调用组合起来:
Date earliest = least(least(a, b), c);

实际上,您可以将此方法变为任何 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));
}

21

Java 8+一行代码。为了让它更安全,加入了空值检查。可以传递任意数量的日期。

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

不是 null 安全的,但更短:

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

没有新方法的非空安全性:

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

2
如果您使用的是Java或更高版本,则不应使用Date。该类设计不佳且已过时。根据确切的要求,使用Instant或来自java.time,现代Java日期和时间API的另一个类。即使在Java 6或7上,您也可以考虑使用ThreeTen Backport - Ole V.V.
1
使用 Java 8 或更高版本 - Ole V.V.
@OleV.V. 我更喜欢在大多数情况下使用 Date,因为在使用 Spring MVC (spring.jackson.serialization.write-dates-as-timestamps=true) 的 DTO 中使用它没有任何问题,在 JavaScript(Angular、React 等)和 Java 之间使用 REST 时也没有时区问题。在 JS 中,只需使用 new Date(timeFromServer) 而无需任何解析器。当 Date 来自前端并与 Hibernate(也是 Date)一起进入数据库以及反之时,您不必考虑时区。它还可以完美地与 Swagger(OpenAPI)配合使用。所有新的和旧的框架都对其提供了很好的支持。 新的时间 API 对于时间计算非常完美。 - pavelety
“日期”在多年中引起了无数的时区错误。Hibernate 5 对java.time的支持非常好。jackson-modules-java8也是如此。口味因人而异。 - Ole V.V.

8

“Efficient”有不同的含义,但我认为比较三个日期不会存在效率问题。事实上,这很便宜。您可以尝试以下方法:

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

或者,更加优雅的方式:
for (Date date : someDates) {
   if (date != null) {
      dates.add(date);
   }
}
Date earliest = dates.first();

但它对所有元素进行排序,这在寻找最小值的任务中是多余的。 - undefined

8
当Apache Commons可用时,您可以使用ObjectUtils.min来获取最小值: ObjectUtils.min
Date earliest = ObjectUtils.min(a, b, c);

最短解决方案 +1 - undefined

6

一些使用流的Java 8方法。第一个方法将在比较之前过滤掉null值,第二个方法将把它们放在列表的末尾。

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

或者

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

6

与此同时,您可能正在使用Java 8 LocalDate(而不是旧的java.util.Date)进行工作。

对于Java 8 LocalDate,请使用此方法获取日期列表中最早的日期:

import java.time.LocalDate;

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

1
我的答案特别涵盖了Java 8 LocalDate,而其他答案没有。 - yglodt
我没有注意到。好的,我把我的踩改成了赞,并删除了我的评论。我建议您添加一些散文来解释您对LocalDate的使用、其优点以及您的答案如何不同且更好。 - Basil Bourque

2

使用 before 和 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;
    }

2

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

如果您的数组不包含NULL值,您可以使用Ordering
Ordering.natural().min(date1, date2, etc);

1

另一种方法是使用 java.util.Collections.min(collection)

返回: 给定集合中根据其元素的自然顺序的最小元素。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接