Недавно я решил следующую проблему:

Учитывая хронологически упорядоченный список LocalDateTime, найдите среднюю продолжительность между соседями.

Я сделал следующее:

@Test
public void canCalculateAverageDuration() {
    final LocalDateTime now = LocalDateTime.now();
    final List<LocalDateTime> localDateTimes = Arrays.asList(now, now.minusHours(5), now.plusMinutes(2));

    final List<Duration> durations = new ArrayList<>();
    localDateTimes.stream()
        .sorted()
        .reduce((first, second) -> {
            durations.add(Duration.between(first, second));
            return second;
        });

    final OptionalDouble averageNanos = durations.stream()
            .mapToDouble(Duration::toNanos)
            .average();

    final Duration average = Duration.ofNanos((long) averageNanos.orElse(0.0));
    assertThat(average).isEqualTo(Duration.parse("PT2H31M"));
}

Интересно, можно ли решить проблему более элегантным способом, например: я бы хотел по возможности избегать списка длительностей. Что вы думаете?

2
Martin 14 Мар 2018 в 13:33

2 ответа

Лучший ответ

Я только что нашел это:

Collections.sort(localDateTimes);
final double average = IntStream.range(0, localDateTimes.size() - 1)
    .mapToLong(l ->
        Duration.between(
            localDateTimes.get(l),
            localDateTimes.get(l+1))
        .toNanos())
    .average().orElse(0.0);
assertThat(Duration.ofNanos((long) average)).isEqualTo(Duration.parse("PT2H31M"));
0
Martin 16 Мар 2018 в 10:09

Вы можете решить эту проблему, просто используя итерации (т.е. не используя потоки):

@Test
public void canCalculateAverageDuration() {
  final LocalDateTime now = LocalDateTime.now();
  final List<LocalDateTime> localDateTimes = Arrays.asList(
      now,
      now.minusHours(5),
      now.plusMinutes(2)
  );
  localDateTimes.sort(Comparator.naturalOrder());

  LocalDateTime previous = null;
  LongSummaryStatistics stats = new LongSummaryStatistics();
  for (LocalDateTime dateTime : localDateTimes) {
    if (previous == null) {
      previous = dateTime;
    }
    else {
      stats.accept(Duration.between(previous, dateTime).toNanos());
    }
  }

  final Duration average = Duration.ofNanos((long) Math.ceil(stats.getAverage()));

  assertThat(average).isEqualTo(Duration.parse("PT2H31M"));
}

Будет ли это более элегантным, зависит от личных предпочтений, но эта версия по крайней мере не использует промежуточных коллекций.

1
M. Prokhorov 14 Мар 2018 в 12:00