代码之家  ›  专栏  ›  技术社区  ›  Sietse

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

  •  6
  • Sietse  · 技术社区  · 16 年前

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

    例子: 2008—10-1 2008—10-2 2008—10-4

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

    最好的方法是什么?

    6 回复  |  直到 8 年前
        1
  •  6
  •   Jean    16 年前

    Sietse de Kaper解决方案假定 颠倒 排序列表,绝对不是最自然的东西

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

    从你的例子来看,

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

    如果另一个开发人员使用您的方法和一个幼稚的方法,他将得到2008-10-01,这不是预期的结果。

  • 不要对列表的顺序做假设。
  • 如果出于性能原因必须这样做,请尝试遵循最自然的约定(升序排序)
  • 如果你真的必须遵循另一个惯例,你真的应该把它记录下来。
    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答案,但我认为它可能会稍慢一些,因为它相当于对数据进行排序,然后查找它=>n log(n)进行排序,然后文档暗示它是用于访问的日志(n),因此它将是n log(n)+log(n)对n

  •     2
  •  3
  •   Keeg    16 年前
    private Date getDateNearest(List<Date> dates, Date targetDate){
        return new TreeSet<Date>(dates).lower(targetDate);
    }
    

    不需要预先排序的列表,treesort修复了这个问题。如果找不到,它将返回空值,因此如果有问题,您必须修改它。效率也不确定:p

        3
  •  2
  •   Sietse    16 年前

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

    private Date getDateNearest(List<Date> dates, Date targetDate){
      for (Date date : dates) {
        if (date.compareTo(targetDate) <= 0) return date;
      }
    
      return targetDate;
    }
    
        4
  •  1
  •   Daniel Hiller    16 年前

    虽然keeg的答案在1.5的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 );
      }
    
    }
    
        5
  •  1
  •   Community CDub    8 年前

    NavigableSet::lower

    这个 Answer by Keeg 简洁明了。这里的想法是利用 lower 中定义的方法 NavigableSet 接口并在 TreeSet 班级。

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

    旧的问题和答案都在使用 java.util.Date 这是时间线上以UTC表示的一个时刻,代表两个日期 一天中的某个时间,或者 java.sql.Date 它笨拙地扩展了util.date,同时假装它没有一天中的某个时间。混乱不堪。

    Java.时间

    那些麻烦的老阶级已经被 Java.时间 构建到Java 8和以后的类。见 Oracle Tutorial . 许多功能已经被移植到Java 6和7中。 ThreeTen-Backport 并进一步适应安卓 ThreeTenABP .

    LocalDate

    这个 LocalDate 类表示没有时间和时区的仅日期值。当这些对象不存储时区时,请注意时区( ZoneId )在确定当前日期时至关重要。在任何给定的时刻,日期都会随着时区的不同而变化。

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

    国际标准化组织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.
    
        6
  •  0
  •   Aidos    16 年前

    你看过JodatimeAPI吗?我好像记得有这样的功能。