使用java 8 streams api确定对象之间的关系

时间:2018-03-14 10:33:54

标签: java java-8 java-stream reduce

最近我解决了以下问题:

给定按时间顺序排列的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 个答案:

答案 0 :(得分:1)

你可以使用迭代解决这个问题(即不使用Streams):

@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 :(得分:0)

我刚刚发现了这个:

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"));