Last active
February 8, 2021 13:01
-
-
Save kamchy/5b2b7f938b779391c8a401c842e98793 to your computer and use it in GitHub Desktop.
Example usage of java 8 features: lambdas, streams and java.time
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.kamilachyla; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.time.Duration; | |
import java.time.LocalDate; | |
import java.time.format.DateTimeFormatter; | |
import java.util.List; | |
import java.util.StringJoiner; | |
import java.util.function.Consumer; | |
import java.util.function.Function; | |
import java.util.stream.Collectors; | |
import java.util.stream.Stream; | |
public class DateTimeSeries { | |
private static final Function<String, LocalDate> toLocalDate = | |
(String s) -> LocalDate.parse(s, DateTimeFormatter.ISO_LOCAL_DATE); | |
private static final Consumer<RunningAverage> averagePeriodPrinter = | |
(RunningAverage avg) -> System.out.print(String.format("Average period length: %,.2f%n", avg.average())); | |
private static final Consumer<RunningAverage> nextDatePredictor = | |
(RunningAverage ra) -> System.out.println( | |
String.format("Predicted next date: %s", ra.previous.plusDays(Math.round(ra.average())))); | |
public static void main(String[] args) { | |
try (BufferedReader is = new BufferedReader(new InputStreamReader(System.in))) { | |
Stream<String> lines = is.lines().filter(s -> !s.isEmpty()); | |
List<LocalDate> dates = lines.map(toLocalDate).collect(Collectors.toList()); | |
RunningAverage average = calculateAveragePeriod(dates); | |
averagePeriodPrinter.andThen(nextDatePredictor).accept(average); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
private static RunningAverage calculateAveragePeriod(List<LocalDate> dates) { | |
return dates.stream().reduce(RunningAverage.initial(), RunningAverage::updated, RunningAverage::plus); | |
} | |
/** | |
* Represents running average of durations between dates. | |
* Provides average() method for calculating average duration in days | |
* for all accumulated durations. It should be called after two calls to update() | |
* (otherwise it throws if called before *second* call to update). | |
* | |
* First call to update() sets up *previous* date field. | |
* Subsequent calls update periodSum (sum of durations) and count (of durations encountered). | |
* */ | |
private static final class RunningAverage { | |
private final LocalDate previous; | |
private final double periodSum; | |
private final int count; | |
static RunningAverage initial() { | |
return new RunningAverage(null, 0, 0); | |
} | |
private RunningAverage(LocalDate initial, double periodSum, int count) { | |
this.previous = initial; | |
this.periodSum = periodSum; | |
this.count = count; | |
} | |
@Override | |
public String toString() { | |
return new StringJoiner(", ", RunningAverage.class.getSimpleName() + "[", "]") | |
.add("previous=" + previous) | |
.add("periodSum=" + periodSum) | |
.add("count=" + count) | |
.add("avg=" + (count > 0 ? String.format("%,.2f", average()) : "unknown")) | |
.toString(); | |
} | |
RunningAverage updated(LocalDate next) { | |
double daysFromPrevToNext = previous == null ? 0 : Duration.between(previous.atStartOfDay(), next.atStartOfDay()).toDays(); | |
return new RunningAverage( | |
next, | |
periodSum + daysFromPrevToNext, | |
count + (previous != null ? 1 : 0)); | |
} | |
double average() { | |
if (count <= 0) { | |
throw new IllegalArgumentException("Count should be greater than 0"); | |
} | |
return periodSum / count; | |
} | |
RunningAverage plus(RunningAverage avg2) { | |
return new RunningAverage(avg2.previous, periodSum + avg2.periodSum, count + avg2.count); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment