我想创建一个按
LocalDate
和 String
排序并按持续时间聚合的人员列表。
我做了一些关于流中持续时间的使用的研究:
以及使用流进行分组并将对象列表减少为子组:
但是,我不可能解决我的问题,类
Person
看起来像这样:
class Person {
private LocalDate startDate;
private String personType;
private Duration time1;
private Duration time2;//constructor, getter+setter, other methods
}
创建的列表的示例如下所示:
List<Person> personList = Arrays.asList(
new Person("2023-02-02","member1","08:00","4:00"),
new Person("2023-02-02","member1","50:00","0:45"),
new Person("2023-02-02","member2","10:00","0:40"),
new Person("2023-02-02","member2","01:00","1:20"),
new Person("2023-02-03","member1","08:00","2:00"),
new Person("2023-02-03","member1","10:00","0:45"),
new Person("2023-02-03","member2","10:00","1:40"),
new Person("2023-02-03","member2","02:00","1:20"),//...
);
我想创建一个按开始日期和人员类型排序的人员列表,以及持续时间的总和。
所需输出:
("2023-02-02","member1","58:00","4:45"),
("2023-02-02","member2","11:00","2:00"),
("2023-02-03","member1","18:00","2:45"),
("2023-02-03","member2","12:00","3:00"),
...
我的方法是使用这样的东西。但是,我无法映射持续时间和字符串值:
Map<LocalDate,List<Person>> result=personList.stream()
.collect(Collectors.groupingBy(Person::getstartDate))
.entrySet().stream()
.collect(Collectors.toMap(x -> {
//how to sum duration values in here?
// Duration duration1 = x.getValue().stream() ...;
//how to use String values in here?
// String string = x.getValue().stream()....
return new Person(x.getKey(), string, duration1,duration2);
}, Map.Entry::getValue));
我发现您熟悉 Java 流 groupingBy API。根据您提供的所需输出,我会尝试以下方法。
首先使用 groupingBy 收集器通过 startDate 和 personType 的组合键对人员进行分组。然后,对于每个组,它对持续时间求和并使用求和值创建一个新的 Person 对象。最后,将生成的 Person 对象收集到一个列表中。 Person.java 类中的代码应该是这样的:
class Person {
.
.
.
public static Person sumDurationsAndTypes(List<Person> persons) {
LocalDate startDate = persons.get(0).getStartDate();
String personType = persons.get(0).getPersonType();
Duration sumTime1 = Duration.ZERO;
Duration sumTime2 = Duration.ZERO;
for (Person person : persons) {
sumTime1 = sumTime1.plus(person.getTime1());
sumTime2 = sumTime2.plus(person.getTime2());
}
return new Person(startDate, personType, sumTime1, sumTime2);
}
@Override
public String toString() {
return "(" + startDate + "," + personType + "," + formatDuration(time1) + "," + formatDuration(time2) + ")";
}
private String formatDuration(Duration duration) {
long hours = duration.toHours();
long minutes = duration.minusHours(hours).toMinutes();
return String.format("%02d:%02d", hours, minutes);
}
sumDurationsAndTypes 方法计算每个人员组的持续时间总和,toString 方法格式化持续时间以进行打印。
然后您可以使用它来检查一切是否按您想要的方式工作:
public class Main {
public static void main(String[] args) {
List<Person> personList = Arrays.asList(
new Person(LocalDate.parse("2023-02-02"), "member1", Duration.ofHours(8), Duration.ofMinutes(4)),
// Add more persons here...
);
Map<String, List<Person>> grouped = personList.stream()
.collect(Collectors.groupingBy(person -> person.getStartDate() + person.getPersonType()));
List<Person> resultList = grouped.values().stream()
.map(Person::sumDurationsAndTypes)
.collect(Collectors.toList());
resultList.forEach(System.out::println);
}
}
如果您需要更多帮助,请告诉我!
祝你好运:)
==============
编辑:
要获取每日分组,您需要按 startDate 进一步分组,并使用 flatMap 为 startDate 和 personType 的每个组合创建单独的条目。我还通过删除不必要的地图来改进您的代码。
public class Main {
public static void main(String[] args) {
List<Person> personList = Arrays.asList(
new Person(LocalDate.parse("2023-02-02"), "member1", Duration.ofHours(8), Duration.ofMinutes(4)),
new Person(LocalDate.parse("2023-02-02"), "member1", Duration.ofHours(0), Duration.ofMinutes(45)),
new Person(LocalDate.parse("2023-02-02"), "member2", Duration.ofHours(10), Duration.ofMinutes(40)),
new Person(LocalDate.parse("2023-02-02"), "member2", Duration.ofHours(1), Duration.ofMinutes(20)),
new Person(LocalDate.parse("2023-02-03"), "member1", Duration.ofHours(8), Duration.ofMinutes(2)),
new Person(LocalDate.parse("2023-02-03"), "member1", Duration.ofHours(10), Duration.ofMinutes(45)),
new Person(LocalDate.parse("2023-02-03"), "member2", Duration.ofHours(10), Duration.ofMinutes(100)),
new Person(LocalDate.parse("2023-02-03"), "member2", Duration.ofHours(2), Duration.ofMinutes(20))
);
List<Person> resultList = personList.stream()
.collect(Collectors.groupingBy(
p -> Arrays.asList(p.getStartDate(), p.getPersonType()),
Collectors.reducing(
new Person(LocalDate.now(), "", Duration.ZERO, Duration.ZERO),
(p1, p2) -> new Person(
p1.getStartDate(),
p1.getPersonType(),
p1.getTime1().plus(p2.getTime1()),
p1.getTime2().plus(p2.getTime2())
)
)
))
.values()
.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
// Print the result
resultList.forEach(p -> {
System.out.println(
p.getStartDate() + "," +
p.getPersonType() + "," +
formatDuration(p.getTime1()) + "," +
formatDuration(p.getTime2())
);
});
}
public static String formatDuration(Duration duration) {
long hours = duration.toHours();
long minutes = duration.minusHours(hours).toMinutes();
return String.format("%02d:%02d", hours, minutes);
}
}
我得到了这个结果列表:
(2023-02-02,member1,08:00,00:49)
(2023-02-02,member2,11:00,02:00)
(2023-02-03,member1,18:00,02:47)
(2023-02-03,member2,12:00,03:20)
每行对应于 startDate 和 personType 的唯一组合,并且对每个组合的持续时间进行求和。
一种方法是进行级联分组并减少持续时间的总和。以下结果是一个 2 级映射,其中包含每个日期和类型的总和:
Map<LocalDate, Map<String, Optional<Person>>> perDateAndTypeAggregated =
personList.stream().collect(Collectors.groupingBy(
Person::getStartDate,
Collectors.groupingBy(
Person::getType,
Collectors.reducing((p1, p2) ->
// this is where we sum the durations
new Person(p1.getStartDate(), p1.getType(), p1.getTime1().plus(p2.getTime1()), p1.getTime2().plus(p2.getTime2()))
)
)
));
如果你想要一个列表(地图提供了更丰富的信息,但这是你的应用程序),你可以按如下方式对之前的地图进行展平和排序:
Comparator<Person> byDateThenType =
Comparator.comparing(Person::getStartDate).thenComparing(Person::getType);
List<Person> result =
perDateAndTypeAggregated.values().stream()
.flatMap(m -> m.values().stream())
.filter(Optional::isPresent)
.map(Optional::get)
.sorted(byDateThenType)
.toList();