java 在日期列表中找到最接近目标的日期的最佳方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/186311/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 11:20:32  来源:igfitidea点击:

Best way to find date nearest to target in a list of dates?

javadate

提问by Sietse

I have a list of Date objects, and a target Date. I want to find the date in the list that's nearest to the target date, but only dates that are before the target date.

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

Example: 2008-10-1 2008-10-2 2008-10-4

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

With a target date of 2008-10-3, I want to get 2008-10-2

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

What is the best way to do it?

最好的方法是什么?

回答by Keeg

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

Doesn't require a pre-sorted list, TreeSort fixes that. It'll return null if it can't find one though, so you will have to modify it if that's a problem. Not sure of the efficency either :P

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

回答by Jean

Sietse de Kaper solution assumes a reversesorted list, definitely not the most natural thing to have around

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

The natural sort order in java is following the ascending natural ordering. (see Collection.sort http://java.sun.com/j2se/1.5.0/docs/api/java/util/Collections.html#sort(java.util.List)documentation)

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

From your example,

从你的例子来看,

target date = 2008-10-03 
list = 2008-10-01 2008-10-02 2008-10-04 

If another developper uses your method with a naive approach he would get 2008-10-01 which is not what was expected

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

  • Don't make assumptions as to the ordering of the list.
  • 不要对列表的顺序做出假设。
  • If you have to for performance reasons try to follow the most natural convention (sorted ascending)
  • 如果出于性能原因必须尝试遵循最自然的约定(按升序排序)
  • If you really have to follow another convention you really should document the hell out of it.

    private Date getDateNearest(List<Date> 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;
    }
    

    edit - I also like the Treeset answer but I think it might be slightly slower as it is equivalent to sorting the data then looking it up => nlog(n) for sorting and then the documentation implies it is log(n) for access so that would be nlog(n)+log(n) vs n

  • 如果你真的必须遵循另一个约定,你真的应该把它记录下来。

    private Date getDateNearest(List<Date> 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

  • 回答by Sietse

    I currently use the following method, but I'm not sure it's the most effective one, because this assumes an already sorted list, and (potentially) iterates over every single date in the list.

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

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

    回答by Basil Bourque

    NavigableSet::lower

    NavigableSet::lower

    The Answer by Keegis cleverly brief. The idea there is to make use of the lowermethod defined in the NavigableSetinterface and implemented in the TreeSetclass.

    Keeg回答非常简洁。那里的想法是利用lowerNavigableSet接口中定义并在TreeSet类中实现的方法。

    But like the other answers it uses the old outmoded date-time classes bundled with the earliest versions of Java. Below is an updated version using java.timeclasses.

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

    The old question and answers are using either java.util.Datewhich is a moment on the timeline in UTC representing both a date anda time-of-day, or the java.sql.Datewhich awkwardly extends util.Date while pretending it does not have a time-of-day. A confusing mess.

    旧的问题和答案使用的是java.util.DateUTC 时间线上的一个时刻,代表日期时间,或者java.sql.Dateutil.Date 笨拙地扩展 util.Date 同时假装它没有时间。一团乱麻。

    java.time

    时间

    Those troublesome old classes have been supplanted by the java.timeclasses built into Java 8 and later. See Oracle Tutorial. Much of the functionality has been back-ported to Java 6 & 7 in ThreeTen-Backportand further adapted to Android in ThreeTenABP.

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

    LocalDate

    LocalDate

    The LocalDateclass represents a date-only value without time-of-day and without time zone. While these objects store no time zone, note that time zone (ZoneId) is crucial in determining the current date. For any given moment the date varies around the globe by time zone.

    LocalDate级表示没有时间一天和不同时区的日期,唯一的价值。虽然这些对象不存储时区,但请注意时区 ( ZoneId) 对确定当前日期至关重要。对于任何给定时刻,日期在全球各地因时区而异。

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

    ISO 8601

    ISO 8601

    Tip: Pad those month and day-of-month numbers with a leading zero. This makes them comply with the ISO 8601standard date-time formats. These formats are used by default in java.time when parsing/generating strings that represent date-time values.

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

    So use 2008-10-01rather than 2008-10-1. If padding is not feasible, parse using DateTimeFormatter.

    所以使用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.
    

    回答by Daniel Hiller

    Although the answer from Keeg is valid in 1.6 in 1.5 there is no method lower() (We're unfortunate to develop against 1.5 :-( )

    尽管 Keeg 的答案在 1.5 中的 1.6 中有效,但没有方法 lower() (我们很遗憾针对 1.5 进行开发 :-( )

    this one works in 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 );
      }
    
    }
    

    回答by Aidos

    Have you looked at the JodaTime API? I seem to recall a feature like this being available.

    你看过 JodaTime API 吗?我似乎记得有这样一个功能可用。