【问题标题】:How to check if a list of intervals (Joda-Time) fully covers a month in Java如何检查间隔列表(Joda-Time)是否完全涵盖Java中的一个月
【发布时间】:2014-02-19 17:09:21
【问题描述】:

我在 Java 中使用 Joda-Time 库来跟踪时间列表 intervals。我想检查Interval 对象列表是否完全涵盖了一个月的每一分钟。大约有 30 个不同长度的间隔,从几小时到几天不等。

我认为一种廉价的方法是按开始时间对间隔列表进行排序,然后依次检查该月范围内的间隔之间是否存在中断。如果是,则该月未完全涵盖。

我被困在第一部分,对列表进行排序。我计划使用Arrays.sort(),但它需要元素来实现可比较的接口。不过看了源码,Joda-Time的Interval类好像没有可以覆盖的,也没法扩展写自己的compareTo方法。

除了编写我自己的排序方法之外,有谁知道更简单的方法来完成这个?谢谢

【问题讨论】:

    标签: java jodatime


    【解决方案1】:

    您可以使用Collections#sortComparator 对时间间隔进行排序,并使用Interval#gap 检查间隙。如果 timeIntervals 完全覆盖 start 和 end 之间的间隔,则以下代码返回 true:

    public static boolean isCovered(List<Interval> timeIntervals, DateTime start, DateTime end) {
        if (timeIntervals.isEmpty()) {
            return false;
        }
    
        // create sorted set from original List to get
        // sorted intervals without double entries
        Set<Interval> intervalSet = new TreeSet<>(new Comparator<Interval>() {
            @Override
            public int compare(Interval o1, Interval o2) {
                return o1.getStart().compareTo(o2.getStart());
            }
        });
        intervalSet.addAll(timeIntervals);
    
        List<Interval> intervals = new ArrayList<>(intervalSet);
    
        // remove intervals completely contained in others
        List<Interval> removedIntervals = new ArrayList<>();
        for (int i = 0; i < (intervals.size()-1); i++) {
            for (int j = (i+1); j < intervals.size(); j++) {
                if (intervals.get(i).contains(intervals.get(j))) {
                    if (!removedIntervals.contains(intervals.get(j))) {
                        removedIntervals.add(intervals.get(j));
                    }
                }
            }
        }
        intervals.removeAll(removedIntervals);
    
        if (intervals.get(0).getStart().isAfter(start) ||
                intervals.get(intervals.size() - 1).getEnd().isBefore(end)) {
            return false;
        }
    
        // check for gaps
        for (int i = 0; i < (intervals.size() - 1); i++) {
        Interval gap = intervals.get(i).gap(intervals.get(i+1));
            if (gap != null && new Interval(start, end).overlaps(gap)) {
                // gap detected between interval i and interval (i+1)
                return false;
            }
        }
    
        return true;
    }
    

    【讨论】:

    • 我不能在评论中画这个)
    • @Ilya 好的,你能用文字表达吗?如果有错误,我想知道,以便我可以更正;)
    • 间隔:​​1) 3 - 10, 2) 4 - 5, 3) 6-11。 2 和 3 有间隙,所以这将返回 false
    • @Ilya 你是对的,感谢您指出这一点。我会解决的。
    【解决方案2】:

    你可以使用下一个方法

    static boolean covers(Interval month, List<Interval> intervals)
    {
      //assumes intervals are sorted already on start times
      final MutableInterval monthInterval = new MutableInterval(month);
      start: for (final Interval interval : intervals)
      {
         if (interval.getStartMillis() <= monthInterval.getStartMillis()
            && interval.getEndMillis() > monthInterval.getStartMillis())
         {
            if (interval.getEndMillis() > monthInterval.getEndMillis())
            {
               return true;
            }
            monthInterval.setStartMillis(interval.getEndMillis());
            // continue start;  // loop continues regardless
         } else {
               if (interval.overlaps(month)) return false;
         }
      }
      return monthInterval.getStartMillis()== monthInterval.getEndMillis();
    } 
    

    示例

       static final List<Interval> intervals = new ArrayList<Interval>();
       static
       {
          intervals.add(new Interval(new DateTime(1990, 05, 15, 00, 00, 00, 00), new DateTime(1990, 05, 18, 00, 00, 00, 00)));
          intervals.add(new Interval(new DateTime(1990, 04, 28, 00, 00, 00, 00), new DateTime(1990, 05, 18, 00, 00, 00, 00)));
          intervals.add(new Interval(new DateTime(1990, 05, 17, 00, 00, 00, 00), new DateTime(1990, 05, 21, 00, 00, 00, 00)));
          intervals.add(new Interval(new DateTime(1990, 05, 21, 00, 00, 00, 00), new DateTime(1990, 05, 29, 00, 00, 00, 00)));
          intervals.add(new Interval(new DateTime(1990, 05, 22, 00, 00, 00, 00), new DateTime(1990, 05, 25, 00, 00, 00, 00)));
          intervals.add(new Interval(new DateTime(1990, 05, 27, 00, 00, 00, 00), new DateTime(1990, 06, 02, 00, 00, 00, 00)));
       }
    
       public static void main(String[] args)
       {
          final DateTime startOfMonth = new DateTime(1990, 05, 01, 00, 00);
          final Interval monthInterval = new Interval(startOfMonth, startOfMonth.plusMonths(1));
    
          covers(monthInterval, intervals);
       }
    
       static boolean covers(Interval month, List<Interval> intervals)
       {
          final MutableInterval monthInterval = new MutableInterval(month);
          start: for (final Interval interval : intervals)
          {
             if (interval.getStartMillis() <= monthInterval.getStartMillis()
                && interval.getEndMillis() > monthInterval.getStartMillis())
             {
                if (interval.getEndMillis() > monthInterval.getStartMillis())
                {
                   return true;
                }
                monthInterval.setStartMillis(interval.getEndMillis());
                continue start;
             }
          }
          return monthInterval.getStartMillis()== monthInterval.getEndMillis();
       }
    

    【讨论】:

    • 因为你有一个循环,你不需要那个标志来继续。
    • 在最初使用 Katja 的建议对区间进行排序后,我使这个解决方案发挥了作用。否则,这个算法无法判断差距在哪里。然后在第一个 if 语句失败时返回 false ,因为它表示前一个间隔和当前间隔之间存在间隙。感谢您的帮助!
    猜你喜欢
    • 2021-04-08
    • 1970-01-01
    • 1970-01-01
    • 2021-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-15
    • 1970-01-01
    相关资源
    最近更新 更多