在日期列表中查找距离目标最近的日期的最佳方法是什么?

我有一个Date对象列表和一个目标Date。 我想在列表中找到最接近目标日期的日期,但只查找目标日期之前的日期。

示例:2008-10-1 2008-10-2 2008-10-4

目标日期为2008-10-3,我希望得到2008-10-2

最好的方法是什么?

Sietse de Kaper解决方案假设一个反向排序列表,绝对不是最自然的东西

java中的自然排序顺序遵循升序自然排序。 (参见Collection.sort http://java.sun.com/j2se/1.5.0/docs/api/java/util/Collections.html#sort(java.util.List)文档)

从你的例子来看,

目标日期= 2008-10-03 
 list = 2008-10-01 2008-10-02 2008-10-04 

如果另一个开发者以一种天真的方法使用你的方法,他将得到2008-10-01,这不是预期的

  • 不要对列表的顺序做出假设。
  • 如果由于性能原因必须遵循最自然的约定(按升序排序)
  • 如果你真的必须遵循另一个约定,你真的应该记录下来。
  • private Date getDateNearest(List dates, Date targetDate){ Date returnDate = targetDate for (Date date : dates) { // if the current iteration'sdate is "before" the target date if (date.compareTo(targetDate) <= 0) { // if the current iteration's date is "after" the current return date if (date.compareTo(returnDate) > 0){ returnDate=date; } } } return returnDate; } 

    编辑 – 我也喜欢Treeset的答案,但我认为它可能稍慢,因为它相当于排序数据然后查找它=> nlog(n)进行排序,然后文档暗示它是log(n)进行访问所以这将是nlog(n)+ log(n)vs n

     private Date getDateNearest(List dates, Date targetDate){ return new TreeSet(dates).lower(targetDate); } 

    不需要预先排序的列表,TreeSort修复了这个问题。 如果它找不到它,它将返回null,因此如果这是一个问题,你将不得不修改它。 不确定效率:P

    我目前使用以下方法,但我不确定它是最有效的方法,因为这假设已经排序的列表,并且(可能)迭代列表中的每个日期。

     private Date getDateNearest(List dates, Date targetDate){ for (Date date : dates) { if (date.compareTo(targetDate) <= 0) return date; } return targetDate; } 

    尽管Keeg的答案在1.6中的1.6中是有效的,但是没有方法lower()(我们不幸的是开发对1.5 :-()

    这个工作在1.5

     import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.TreeSet; public class GetNearestDate { public static void main( String[] args ) throws ParseException { final SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "dd.MM.yyyy HH:mm:ss" ); List< Date > otherDates = Arrays.asList( new Date[]{ simpleDateFormat.parse( "01.01.2008 01:00:00" ) , simpleDateFormat.parse( "01.01.2008 01:00:02" ) } ); System.out.println( simpleDateFormat.parse( "01.01.2008 01:00:00" ).equals( get( otherDates , simpleDateFormat.parse( "01.01.2008 01:00:01" ) ) ) ); System.out.println( simpleDateFormat.parse( "01.01.2008 01:00:02" ).equals( get( otherDates , simpleDateFormat.parse( "01.01.2008 01:00:03" ) ) ) ); System.out.println( null == get( otherDates , simpleDateFormat.parse( "01.01.2008 01:00:00" ) ) ); } public static Date get( List< Date > otherDates , Date dateToApproach ) { final TreeSet< Date > set = new TreeSet< Date >( otherDates ); set.add( dateToApproach ); final ArrayList< Date > list = new ArrayList< Date >( set ); final int indexOf = list.indexOf( dateToApproach ); if ( indexOf == 0 ) return null; return list.get( indexOf - 1 ); } } 

    你看过JodaTime API吗? 我似乎记得这样的function可用。

    NavigableSet::lower

    Keeg的回答非常简洁。 这个想法是利用NavigableSet接口中定义的lower方法并在TreeSet类中实现。

    但与其他答案一样,它使用与最早版本的Java捆绑在一起的旧的过时日期时间类。 下面是使用java.time类的更新版本。

    旧的问题和答案是使用java.util.Date ,它是UTC中表示日期时间的时间轴上的一个时刻,或者是java.ql.Date,它在假装时笨拙地扩展了util.Date没有时间。 令人困惑的混乱。

    java.time

    那些麻烦的旧类已被Java 8及更高版本中内置的java.time类所取代。 请参阅Oracle教程 。 许多function已经在ThreeTen-Backport中移植到Java 6和7中,并在ThreeTenABP中进一步适应Android。

    LocalDate

    LocalDate类表示没有时间且没有时区的仅日期值。 虽然这些对象不存储时区,但请注意时区( ZoneId )对于确定当前日期至关重要。 对于任何特定时刻,日期在全球范围内按时区变化。

     ZoneId zoneId = ZoneId.of( "America/Montreal" ); LocalDate today = LocalDate.now( zoneId ); // 2016-06-25 

    ISO 8601

    提示:将那些月份和日期数字填充为前导零。 这使它们符合ISO 8601标准的日期时间格式。 在解析/生成表示日期时间值的字符串时,默认情况下在java.time中使用这些格式。

    所以使用2008-10-01而不是2008-10-1 。 如果填充不可行,请使用DateTimeFormatter解析。

     NavigableSet dates = new TreeSet( 3 ); dates.add( LocalDate.parse( "2008-10-01" ); dates.add( LocalDate.parse( "2008-10-02" ); dates.add( LocalDate.parse( "2008-10-04" ); LocalDate target = LocalDate.parse( "2008-10-03" ); LocalDate hit = dates.lower( target ); // Reminder: test for `null == hit` to see if anything found.