Как сравнить экземпляры объектов DateTime? - PullRequest
0 голосов
/ 13 ноября 2018

Я пишу программу для сортировки дат по времени. Существует начало 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/2001 и end 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 голосов
/ 13 ноября 2018

Я предполагаю, что вопрос заключается не только в сортировке, но и в разделении перекрывающихся интервалов на более мелкие сегменты.Вы должны много играть с Интервальная арифметика .

С 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 s сопоставимы по дате начала.Всякий раз, когда пользователь вводит новый Interval (термин), вы должны просмотреть список и проверить, является ли он contains() существующим интервалом или предшествует ему, с помощью isBefore() или isAfter().Если это overlaps(), вы должны быть осторожны, если также проверить, перекрывается ли он со следующим интервалом в списке.

Затем вы можете вызвать fragment(), который объединит 2 интервала в более мелкие.Вы должны быть осторожны, чтобы удалить предыдущие.Так что, возможно, имеет смысл просто просмотреть список и проверить, перекрываются ли они или нет.Если вы дошли до конца, вы все равно можете использовать fragment() для объединения двух непересекающихся интервалов.

0 голосов
/ 13 ноября 2018

ТЛ; др

То, что я хочу сделать, это отсортировать все начало 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 по упорядочению объектов .

Сортировать по дате остановки

Ваш вопрос неясен, но вы, похоже, просите альтернативно отсортировать по дате окончания. Я не могу себе представить, насколько это полезно на практике. Но в любом случае, решение заключается в сортировке, предоставляя реализацию интерфейса 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 );
}

Перед естественной сортировкой: [Term {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}]

После естественной сортировки: [Term {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.

Подход в сравнении - Half-Open, где начало включительно , а окончание эксклюзив . Таким образом, год начинается с первого года и продолжается, но не включает в себя, первый из следующего года. Вы показываете это в своих примерах в вопросе.

0 голосов
/ 13 ноября 2018

Поместите каждую дату в новую коллекцию, отсортируйте ее по дате, а затем создайте новые объекты, состоящие из соседних дат из коллекции.

Попробуйте:

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