Java 使用startDate和endDate按月分组

Java 使用startDate和endDate按月分组,java,java-stream,grouping,Java,Java Stream,Grouping,我有一个类模块,它包含 private LocalDate startDate; private LocalDate endDate; private Map<Teacher, Integer> workedHoursPerDay; 请求是获得按月分组的所有教师的月度预算。预期的回报是Map。我搜索过了,但我不知道如何使用startDate和endDate进行搜索 单个模块的月支出是指分配给该模块的所有教师在该月所有工作日的累计成本 逻辑将是: -从所

我有一个类模块,它包含

private LocalDate startDate;        
private LocalDate endDate;       
private Map<Teacher, Integer> workedHoursPerDay;
请求是获得按月分组的所有教师的月度预算。预期的回报是Map。我搜索过了,但我不知道如何使用startDate和endDate进行搜索

单个模块的月支出是指分配给该模块的所有教师在该月所有工作日的累计成本

逻辑将是: -从所有模块中获取(设置)教授该模块的教师和小时数(Map每天工作小时数)。然后将每位教师的工作时数乘以小时数。最后,使用每个模块的开始日期和结束日期按月对成本进行分组

我做过类似的事情,但没有得到预期的结果

公共地图calculateCumulativeMonthlySpends(){

//获取所有模块的最小开始日期
LocalDate localDateStart=modules.stream()
.flatMap(模块->模块.getWorkingDays().stream())
.min(LocalDate::compareTo).get();
//获取所有模块的最大结束日期
LocalDate localDateEnd=modules.stream()
.flatMap(模块->模块.getWorkingDays().stream())
.max(LocalDate::compareTo).get();
//从开始日期迭代到结束日期,计算所有模块的每日成本
//并将所有模块的所有天数的成本相加
//然后创建一个以moth为键的地图,以及每个月所有模块的成本
//最后,按键对地图进行排序
返回Stream.iterate(localDateStart,d->d.plusDays(1))
.limit(ChronoUnit.DAYS.between(localDateStart,localDateEnd)+1)
.collect(Collectors.toMap(LocalDate::getMonth,day->dailcost(day,modules),Integer::sum))
.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,
(oldValue,newValue)->oldValue,LinkedHashMap::new));
}
/**
*计算所有模块的每日成本
*检查模块的工作日是否包含属性day
*如果是sum(),则将此模块的每日成本添加到其他模块的每日成本中
*
*@param-day
*@param模块
*@返回
*/
专用整数DailCost(LocalDate-day,设置模块){
返回modules.stream()
.filter(模块->模块.getWorkingDays().contains(天))
.flatMap(p->p.getCommittedHoursPerDay().entrySet().stream())
.mapToInt(e->e.getKey().getHourlyWage()*e.getValue())
.sum();
}
代码被修改了。我现在有了预期的结果,但我希望有更好的方法来做到这一点

预计:{一月=19529,二月=26909,三月=35593,四月=60243,五月=79172,六月=70135,七月=72470,八月=40161,九月=21750,十月=8636,
十一月=1344}

如果没有更多信息,很难在代码中找到bug。但是解释一下如何自己找到它可能会对你更有帮助

dailyCost
中的单个表达式太复杂,无法调试。Java流非常棒,但它们有一个非常大的问题:使用交互式调试器进行调试非常困难(很遗憾,没有有用的答案)

秘诀是保持你的运营商链短而明显。在可能的情况下,分为单独的方法,这些方法代表单个责任,并且可以单独进行单元测试。然后,您可以自信地将这些方法构建到解决方案中

例如:

.filter(p -> (p.getStartDate().isBefore( day)  || p.getStartDate().equals(day)))
.filter(p -> p.getEndDate().isAfter(day) || p.getEndDate().equals(day))
可能成为:

.filter(p -> p.wasActiveOnDay(day))
.mapToInt(Module:getTotalWages)
以及:

可能成为:

.filter(p -> p.wasActiveOnDay(day))
.mapToInt(Module:getTotalWages)
每个模块都可以作为
模块
测试的一部分进行单元测试。这将给你留下:

return modules.stream()
    .filter(m -> m.wasActiveOnDay(day))
    .mapToInt(Module::getTotalWages).sum();

那句话更容易调试。

经过调查,我发现了。工作日包括周末,这是错误的

    public Map<Month, Integer> calculateCumulativeMonthlySpends() {

    //Get the min startDate of all the  modules
    LocalDate localDateStart = modules.stream()
            .flatMap(modules -> module.getWorkingDays().stream())
            .min(LocalDate::compareTo).get();
    //Get the max endDate of all the modules
    LocalDate localDateEnd = modules.stream()
            .flatMap(module -> module.getWorkingDays().stream())
            .max(LocalDate::compareTo).get();

    //Iterate from the start date to the end date, calculate the daily cost for all the module
    //and sum the cost of all the days for the all modules
    //Then create a map with moth as a key and the cost of all the modules for each month
    //Finally sort the map by key

    return Stream.iterate(localDateStart, d -> d.plusDays(1))
            .limit(ChronoUnit.DAYS.between(localDateStart, localDateEnd) + 1)
            .collect(Collectors.toMap(LocalDate::getMonth, day -> dailyCost(day, modules), Integer::sum))
            .entrySet()
            .stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (oldValue, newValue) -> oldValue, LinkedHashMap::new));
}

/**
 * Calculate the daily cost for all the modules
 * Check if the modules's working days contain the attribute day
 * If yes sum(),  add the daily cost of this module to the other modules' daily cost
 *
 * @param day
 * @param modules
 * @return
 */
private Integer dailyCost(LocalDate day, Set<Module> modules) {

    return modules.stream()
            .filter(module -> module.getWorkingDays().contains(day))
            .flatMap(p -> p.getCommittedHoursPerDay().entrySet().stream())
            .mapToInt(e -> e.getKey().getHourlyWage() * e.getValue())
            .sum();
}
publicmap calculateCumulativeMonthlySpends(){
//获取所有模块的最小起始日期
LocalDate localDateStart=modules.stream()
.flatMap(模块->模块.getWorkingDays().stream())
.min(LocalDate::compareTo).get();
//获取所有模块的最大结束日期
LocalDate localDateEnd=modules.stream()
.flatMap(模块->模块.getWorkingDays().stream())
.max(LocalDate::compareTo).get();
//从开始日期迭代到结束日期,计算所有模块的每日成本
//并将所有模块的所有天数的成本相加
//然后创建一个以moth为键的地图,以及每个月所有模块的成本
//最后,按键对地图进行排序
返回Stream.iterate(localDateStart,d->d.plusDays(1))
.limit(ChronoUnit.DAYS.between(localDateStart,localDateEnd)+1)
.collect(Collectors.toMap(LocalDate::getMonth,day->dailcost(day,modules),Integer::sum))
.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,
(oldValue,newValue)->oldValue,LinkedHashMap::new));
}
/**
*计算所有模块的每日成本
*检查模块的工作日是否包含属性day
*如果是sum(),则将此模块的每日成本添加到其他模块的每日成本中
*
*@param-day
*@param模块
*@返回
*/
专用整数DailCost(LocalDate-day,设置模块){
返回modules.stream()
.filter(模块->模块.getWorkingDays().contains(天))
.flatMap(p->p.getCommittedHoursPerDay().entrySet().stream())
.mapToInt(e->e.getKey().getHourlyWage()*e.getValue())
.sum();
}
我现在有了例外值,但我认为有更好的方法来做到这一点。我希望你有任何建议


注意:getWorkingDays()没有周末的工作日

因为我们不知道输入,如何
    public Map<Month, Integer> calculateCumulativeMonthlySpends() {

    //Get the min startDate of all the  modules
    LocalDate localDateStart = modules.stream()
            .flatMap(modules -> module.getWorkingDays().stream())
            .min(LocalDate::compareTo).get();
    //Get the max endDate of all the modules
    LocalDate localDateEnd = modules.stream()
            .flatMap(module -> module.getWorkingDays().stream())
            .max(LocalDate::compareTo).get();

    //Iterate from the start date to the end date, calculate the daily cost for all the module
    //and sum the cost of all the days for the all modules
    //Then create a map with moth as a key and the cost of all the modules for each month
    //Finally sort the map by key

    return Stream.iterate(localDateStart, d -> d.plusDays(1))
            .limit(ChronoUnit.DAYS.between(localDateStart, localDateEnd) + 1)
            .collect(Collectors.toMap(LocalDate::getMonth, day -> dailyCost(day, modules), Integer::sum))
            .entrySet()
            .stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (oldValue, newValue) -> oldValue, LinkedHashMap::new));
}

/**
 * Calculate the daily cost for all the modules
 * Check if the modules's working days contain the attribute day
 * If yes sum(),  add the daily cost of this module to the other modules' daily cost
 *
 * @param day
 * @param modules
 * @return
 */
private Integer dailyCost(LocalDate day, Set<Module> modules) {

    return modules.stream()
            .filter(module -> module.getWorkingDays().contains(day))
            .flatMap(p -> p.getCommittedHoursPerDay().entrySet().stream())
            .mapToInt(e -> e.getKey().getHourlyWage() * e.getValue())
            .sum();
}