【问题标题】:Combining Overlapping Date Ranges - Java组合重叠的日期范围 - Java
【发布时间】:2015-11-18 17:13:28
【问题描述】:

我有一个如下所示的 Task 类(使用 Java 8 Time API)。

class Task {
    LocalDateTime start;
    LocalDateTime end;
    Set<String> actionItems;
}

我有两个包含此类任务实例的排序(首先按开始,然后按结束)列表,比如说List&lt;Task&gt; tasksList1List&lt;Task&gt; tasksList2。我想组合重叠的任务(通过在需要时中断任务,并将重叠的其他任务中的 actionItems 添加到单个新任务对象中)。

例如,假设我有一个名为 T1 的任务,该任务从 2015 年 1 月 1 日开始,到 2015 年 1 月 31 日结束,其中包含操作项 A 和 B。然后用户创建了一个从 01 开始的新任务 T2 /15/2015 并于 2015 年 2 月 15 日结束,并将操作项 C 添加到其中。当我组合时,我应该得到三个 Task 对象,如下所示。

  • 任务 X - 从 01/01/2015 到 01/15/2015,包含操作项 A、B
  • 任务 Y - 从 01/15/2015 到 01/31/2015,包含项目 A、B 和 C
  • 任务 Z - 从 01/31/2015 到 02/15/2015,包含项目 C

为了形象化,如果我的两个列表中的任务对象在时间轴中如下所示:

> [-----]      [-----]         [----]         [-----------------]
>     [-----]           [---------------]         [------]

然后生成的任务列表将包含以下任务。

> [--][-][--]  [-----]  [-----][----][--]      [-][------][-----]`

重叠任务应该将两个任务的actionItems组合起来,在它们重叠期间重叠。

处理此问题的最有效方法是什么?目前我正在尝试使用 PeekableIterator 的不同选项,但还没有运气。也欢迎任何使用 JodaTime 而不是 Java 8 API 的解决方案。

【问题讨论】:

    标签: java algorithm datetime time jodatime


    【解决方案1】:

    首先,如果您只关心日期(不关心时间),最好改用LocalDate。其次,我假设您有一个任务构造函数。所以我使用了以下Task 对象:

    static class Task {
        LocalDate start;
        LocalDate end;
        Set<String> actionItems;
    
        public Task(LocalDate start, LocalDate end,
                Collection<String> actionItems) {
            this.start = start;
            this.end = end;
            this.actionItems = new HashSet<>(actionItems);
        }
    
        @Override
        public String toString() {
            return start + ".." + end + ": "+actionItems;
        }
    }
    

    这是更一般任务的解决方案,它只是根据您的规则合并给定集合中的所有任务(输入集合不一定是排序的):

    public static List<Task> convert(Collection<Task> input) {
        NavigableMap<LocalDate, Set<String>> map = new TreeMap<>();
        map.put(LocalDate.MIN, new HashSet<>());
    
        for (Task task : input) {
            if (!map.containsKey(task.start)) {
                map.put(task.start, new HashSet<>(map.lowerEntry(task.start).getValue()));
            }
            if (!map.containsKey(task.end)) {
                map.put(task.end, new HashSet<>(map.lowerEntry(task.end).getValue()));
            }
            for (Set<String> set : map.subMap(task.start, task.end).values()) {
                set.addAll(task.actionItems);
            }
        }
        List<Task> result = new ArrayList<>();
        LocalDate prev = null;
        Set<String> prevValues = Collections.emptySet();
        for (Entry<LocalDate, Set<String>> entry : map.entrySet()) {
            if (!prevValues.isEmpty()) {
                result.add(new Task(prev, entry.getKey(), prevValues));
            }
            prev = entry.getKey();
            prevValues = entry.getValue();
        }
        return result;
    }
    

    核心是NavigableMap,其中每个键是下一个时间段的开始,值是从给定开始到下一个键的时间段内的操作集合(空值对应于没有操作的时间段) .添加新任务后,现有条目会相应更新。用法示例:

    List<Task> res = convert(Arrays.asList(
      new Task(LocalDate.parse("2015-01-01"), LocalDate.parse("2015-01-31"), 
            Arrays.asList("A", "B")),
      new Task(LocalDate.parse("2014-01-01"), LocalDate.parse("2014-01-31"), 
            Arrays.asList("A", "B")),
      new Task(LocalDate.parse("2015-01-15"), LocalDate.parse("2015-02-15"), 
            Arrays.asList("C"))));
    res.stream().forEach(System.out::println);
    

    输出:

    2014-01-01..2014-01-31: [A, B]
    2015-01-01..2015-01-15: [A, B]
    2015-01-15..2015-01-31: [A, B, C]
    2015-01-31..2015-02-15: [C]
    

    【讨论】:

    • 谢谢塔吉尔!你摇滚 :) 对不起,我不清楚我的问题是时间对我来说也很重要。为了简单起见,我把它省略了。我相信该解决方案也适用于 LocalDateTime 吗?还有一点需要注意的是,您的算法的输出与我的预期有些不同。在听三个要点时,我希望输出三个任务,首先是从 01/01 到 01/15 的 A、B,第二个是从 01/15 到 01/31 的 A、B、C,第三个是从 01/31 到02/15 与 C。在输出中不会有重叠。我正在尝试修改您的解决方案以处理该问题...
    • @YohanLiyanage,是的,对于LocalDateTime,它将以相同的方式工作,只需在任何地方替换类名即可。我在使用示例中添加了一个不重叠的任务(2014 年),只是为了测试它是否被正确处理。去掉它,输出还是一样的。
    • 哦,我错过了 2014 年 :)。再次感谢,这就像一个魅力
    猜你喜欢
    • 1970-01-01
    • 2020-12-13
    • 2018-02-19
    • 1970-01-01
    • 1970-01-01
    • 2021-09-28
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多