如何比较实例化DateTime对象?

时间:2018-11-13 15:19:36

标签: java datetime

我正在编写一个按日期对日期时间进行排序的程序。 有一个DateTime开始和一个DateTime结束。 通过用户输入将它们放入自己的对象中。 DateTime的开头和DateTime的结尾组成它们自己的术语。 因此,每个术语都有其自己的对象,其对象的DateTime开头和DateTime结束。

我想要做的是按日期对所有DateTime开头和所有DateTime结尾进行排序。 我怎样才能做到这一点?我正在考虑使用编译器,但似乎无法对自定义对象执行此操作。

因此,可以说用户输入了一个beginning date of 01/01/2000和一个end date of 01/01/2002。这构成一个学期。 然后,用户输入由beginning date of 01/01/2001end date of 01/01/2003组成的第二项。

我现在想要做的是对日期进行排序,并制定三个新的术语:

beginning 01/01/2000 end 01/01/2001

beginning 01/01/2001 end 01/01/2002

beginning 01/01/2002 end 01/01/2003

我对如何进行此操作有任何想法吗?

3 个答案:

答案 0 :(得分:1)

tl; dr

  

我想要做的是按日期对所有DateTime开头和所有DateTime结尾进行排序。

您可以做一个,但不能两个都做。

要按开始日期排序(在实践中似乎很合理),请实施compareTo方法。

return this.getDateRange().getStart().compareTo( thatStart );

要按停止日期排序(我认为这没有任何意义),请实现Comparator界面。

return 
    t1.getDateRange().getEnd().compareTo( 
        t2.getDateRange().getEnd() 
    )
;

LocalDate

正如其他人指出的那样,您应该使用现代的 java.time 类,而不要使用可怕的旧Date / Calendar / SimpleDateFormat类。对于没有日期,没有时区的仅日期值,请使用LocalDate

LocalDateRange

正如Answer by jbx所讨论的那样,您应该将词条的开始日期和结束日期成对表示。但是,如果一个类已经存在,则不要编写它。使用LocalDateRange项目中的ThreeTen-Extra类。该项目为 java.time 类添加了功能。

Comparable

在您的Term类上,实现Comparable接口以启用简单的排序。添加方法compareTo。显而易见的方法是比较每个LocalDate对象的Term对象的起始LocalDateRange

LocalDate类实现了compareTo,不是我们必需的。

@Override
public int compareTo ( Object o ) {
    if ( this == o ) return 0;
    if ( o == null || getClass() != o.getClass() ) return 0;
    LocalDate thatStart = ( ( Term ) o ).getDateRange().getStart();
    return this.getDateRange().getStart().compareTo( thatStart );
}

请参见Java Tutorial on object-ordering

按停止日期排序

您的问题尚不清楚,但是您似乎要在结束日期前进行排序。我无法想象这实际上有什么用。但是无论如何,解决方案是通过提供Comparator接口的实现来进行排序。

    @Override
    public int compare ( Term t1 , Term t2 ) {
        return t1.getDateRange().getEnd().compareTo( t2.getDateRange().getEnd() );
    }

示例类

这里是一个示例Term类。可能不是生产质量的代码,但是应该使您朝着正确的方向前进。

package com.basilbourque.example;

import org.threeten.extra.LocalDateRange;

import java.time.LocalDate;
import java.time.Month;
import java.util.*;

public class Term implements Comparable {
    private UUID id;
    private LocalDateRange dateRange;

    // Constructor
    public Term ( LocalDate start , LocalDate stop , UUID id ) {
        Objects.requireNonNull( start ); // TODO: Add more such checks for all arguments.
        if ( start.getYear() < 2015 ) {  // TODO: Add more such checks for too far into the past or future, for both start and for stop.
            throw new IllegalArgumentException( "Year of start date is too far in the past. Message # afcd30a0-b639-4ccf-b064-18cc2ea8587b." );
        }
        this.id = id;
        this.dateRange = LocalDateRange.of( start , stop );
    }

    // Alternative constructor.
    public Term ( LocalDateRange dateRange , UUID id ) {
        this( dateRange.getStart() , dateRange.getEnd() , id );
    }



    // --------|  Object  |-------------------------
    @Override
    public String toString ( ) {
        return "Term{ " +
                "id=" + id +
                " | dateRange=" + dateRange +
                " }";
    }

    public UUID getId ( ) {
        return id;
    }

    public LocalDateRange getDateRange ( ) {
        return dateRange;
    }

    @Override
    public boolean equals ( Object o ) {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        Term term = ( Term ) o;
        return this.getId().equals( term.getId() );
    }

    @Override
    public int hashCode ( ) {
        return Objects.hash( this.getId() );
    }

    @Override
    public int compareTo ( Object o ) {
        if ( this == o ) return 0;  // If same object.
        if ( o == null || getClass() != o.getClass() ) return 0;
        LocalDate thatStart = ( ( Term ) o ).getDateRange().getStart();
        return this.getDateRange().getStart().compareTo( thatStart );
    }

    static public class StopDateComparator implements Comparator < Term > {

        @Override
        public int compare ( Term t1 , Term t2 ) {
            return t1.getDateRange().getEnd().compareTo( t2.getDateRange().getEnd() );
        }
    }

}

尝试一下。

public static void main ( String[] args ) {
    Term t1 = new Term( LocalDate.of( 2018 , Month.JUNE , 23 ) , LocalDate.of( 2018 , Month.JULY , 23 ) , UUID.randomUUID() );
    Term t2 = new Term( LocalDate.of( 2018 , Month.JANUARY , 23 ) , LocalDate.of( 2018 , Month.DECEMBER , 23 ) , UUID.randomUUID() );
    Term t3 = new Term( LocalDate.of( 2018 , Month.MARCH , 23 ) , LocalDate.of( 2018 , Month.APRIL , 23 ) , UUID.randomUUID() );
    List < Term > terms = new ArrayList <>( List.of( t1 , t2 , t3 ) );
    System.out.println( "Before natural sort: " + terms );
    Collections.sort( terms );
    System.out.println( "After natural sort: " + terms );
    Collections.sort( terms , new Term.StopDateComparator() );
    System.out.println( "After Comparator sort: " + terms );
}
  

在自然排序之前:[术语{id = 27c0b9e6-076f-4ded-9bbd-bf1a2c7914bc | dateRange = 2018-06-23 / 2018-07-23},术语{id = 792bf365-eca4-460b-afad-c5cf62cf9a29 | dateRange = 2018-01-23 / 2018-12-23},术语{id = c49f79e1-11cd-4865-aa46-8fbf3c85dbfd | dateRange = 2018-03-23 / 2018-04-23}]

     

自然排序后:[术语{id = 792bf365-eca4-460b-afad-c5cf62cf9a29 | dateRange = 2018-01-23 / 2018-12-23},术语{id = c49f79e1-11cd-4865-aa46-8fbf3c85dbfd | dateRange = 2018-03-23 / 2018-04-23},字词{id = 27c0b9e6-076f-4ded-9bbd-bf1a2c7914bc | dateRange = 2018-06-23 / 2018-07-23}]

     

在比较器排序后:[Term {id = c49f79e1-11cd-4865-aa46-8fbf3c85dbfd | dateRange = 2018-03-23 / 2018-04-23},字词{id = 27c0b9e6-076f-4ded-9bbd-bf1a2c7914bc | dateRange = 2018-06-23 / 2018-07-23},术语{id = 792bf365-eca4-460b-afad-c5cf62cf9a29 | dateRange = 2018-01-23 / 2018-12-23}]

abuts

如果您的Term对象应该相继运行,则可以使用LocalDateRange::abuts方法进行测试。

比较的方法是半开,其中开始为包含,而结束为排他。因此,一年从一年的第一年开始,一直持续到但不包括以下年的第一年。您在“问题”中的示例中展示了这一点。

答案 1 :(得分:0)

将每个日期放入新集合中,按日期排序,然后创建包含该集合中相邻日期的新对象。

尝试:

public static void main(String[] args) {
    List<YourClass> list = new ArrayList<>();

    list.add(new YourClass(new Date(100000000), new Date(200000000)));
    list.add(new YourClass(new Date(150000000), new Date(250000000)));
    list.add(new YourClass(new Date(50000000), new Date(300000000)));

    System.out.println(list);

    List<Date> dates = new ArrayList<>();
    for (YourClass yc : list){
        if (!dates.contains(yc.beginning)) dates.add(yc.beginning);
        if (!dates.contains(yc.end)) dates.add(yc.end);
    }

    Collections.sort(dates);

    List<YourClass> list2 = new ArrayList<>();

    for (int i=0; i < dates.size() -1; i++){
        list2.add(new YourClass(dates.get(i), dates.get(i+1)));
    }

    System.out.println(list2);

}

public static class YourClass {
    Date beginning;
    Date end;

    public YourClass(Date beginning, Date end) {
        this.beginning = beginning;
        this.end = end;
    }

    @Override
    public String toString() {
        return "\n" + beginning  + " -> " + end ;
    }
}

答案 2 :(得分:0)

我认为问题不仅在于排序,还在于将重叠的间隔分成较小的段。您必须和Interval Arithmetic一起玩。

使用Java 8,您可以通过将“条件”编码为时间间隔开始,时间间隔本身就是Comparable。第二部分是如果用户指定重叠的间隔,则将间隔分成多个。

class Interval implements Comparable<Interval> {
  private final LocalDateTime start;
  private final LocalDateTime end;

  public Interval(LocalDateTime start, LocalDateTime end) {
     this.start = start;
     this.end = end;
  }

  public int compareTo(Interval that) {
    return this.start.compareTo(that.start);
  }

  public boolean overlaps(Interval that) {
    return !this.isBefore(that) && !this.isAfter(that);
  }

  public boolean contains(Interval that) {
    return this.start.isBefore(that.start) && this.end.isAfter(that.end);
  }

  public boolean isBefore(Interval that) {
    return this.end.isBefore(that.start);
  }

  public boolean isAfter(Interval that) {
    return this.start.isAfter(that.end);
  }

  public Set<Interval> fragment(Interval that) {
    if (that.start.isBefore(this.start)) {
      return that.fragment(this);
    }

    Set<Interval> result = new HashSet<>();
    if (this.end.isBefore(that.start)) {
       result.add(this);
       result.add(that);
       result.add(new Interval(this.end, that.start));
    } else if ((this.end.isAfter(that.start) && this.end.isBefore(that.end)) {
       result.add(new Interval(this.start, that.start);
       result.add(new Interval(that.start, this.end);
       result.add(new Interval(this.end, that.end));
    } else if (this.end.isAfter(that.end)) {
       result.add(new Interval(this.start, that.start);
       result.add(new Interval(that);
       result.add(new Interval(that.end, this.end));
    }
  }
}

您现在可以对它们进行排序,因为Interval可以按开始日期进行比较。 每当用户输入新的Interval(期限)时,您都必须遍历该列表,并使用contains()或{{ 1}}。如果是isBefore(),则必须注意是否还要检查它是否与列表中的下一个间隔重叠。

然后可以调用isAfter(),它将把两个间隔合并成较小的间隔。您需要小心删除先前的那些。因此,仅浏览列表并检查它们是否重叠是有意义的。如果到达终点,您仍然可以使用overlaps()来组合两个不相交的间隔。