问题
I have a list of activities(Activity) and I want to determine a data structure of the form Map(String, DateTime) (not Duration or Period; DateTime it's a must) that maps. For each activity the total duration computed over the monitoring period.
The class Activity has: activityLabel(String), startTime(DateTime), endTime(DateTime). I use joda time.
This is what I have done:
Map<String, DateTime> durations = activities.stream().collect(Collectors.toMap(
it -> it.activityLabel,
it ->new DateTime(0,0,0,0,0,0)
//,DateTime::plus
));
I guess I should use DateTime plus(ReadablePeriod period) or DateTime plus(ReadableDuration duration) , but I don't know how to send a parameter of type Duration or Period to the method reference.
How can I achieve this result?
EDIT: For the input:
2011-12-03 01:00:00 2011-12-03 9:00:00 Sleeping
2011-12-04 03:00:00 2011-12-04 10:30:00 Sleeping
I should have the output: Sleeping 0-0-0 15:30:00 (years,months,days,hours,minutes,seconds)
回答1:
The code (using a Period) would look like this:
Map<String, Period> map = activities.stream()
.collect(Collectors.toMap(Activity::activityLabel, ac -> new Period(ac.getStartTime(), ac.getEndTime()),
(left, right) -> left.plus(right)));
If you really want to output that Period as a String, you need PeriodFormatter.
private static PeriodFormatter periodFormatter() {
return new PeriodFormatterBuilder()
.printZeroAlways()
.minimumPrintedDigits(2)
.appendYears().appendSeparator("-")
.appendMonths().appendSeparator("-")
.appendDays().appendLiteral(" ")
.appendHours().appendSeparator(":")
.appendMinutes().appendSeparator(":")
.appendSeconds().toFormatter();
}
And then your code would look more like this:
Map<String, String> map = activities.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
Activity::getLabel,
ac -> new Period(ac.getStartTime(), ac.getEndTime()),
Period::plus),
m -> m.entrySet().stream().collect(Collectors.toMap(
Entry::getKey,
e -> e.getValue().toString(periodFormatter)))));
System.out.println(map); // {Sleeping=00-00-00 15:30:00
回答2:
As you have mentioned in comment that you really need is a DateTime not a Period.
Since the DateTime has no api for DateTime.plus(DateTime)/DateTime.minus(DateTime), but you can plus / minus a Period on a DateTime , then you need a DateTime to start, and the code using Collectors api is replacing toMap with groupingBy which is more efficiently and expressiveness for doing the task in your case:
DateTime start = DateTime.now();
Map<String, DateTime> result = list.stream().collect(Collectors.groupingBy(
it -> it.activityLabel,
Collectors.mapping(
it -> new Period(it.startTime, it.endTime),
// the important section is here:
// 1. merge all of periods by using reducing
// 2. convert a Period to a DateTime by collectingAndThen
Collectors.collectingAndThen(
Collectors.reducing(Period.ZERO, Period::plus),
start::plus
)
)
));
来源:https://stackoverflow.com/questions/44097658/how-to-send-parameters-to-a-reference-method-in-a-stream-java-8