4

I would like to calculate the time between dates in an ArrayList using java 8 lambda expressions. With the time between dates I mean: Date i - Date i-1, Date i-1 - Date i-2, etc. I am using java.util.Date.

I already use this code in java 7, but I am just curious if streaming can shorten this up:

List<Date> dates = transactions.stream().map(Transaction::getTimestampEnd).sorted((d1, d2) -> d1.compareTo(d2)).collect(Collectors.toList());
List<Long> interArrivalTimes = new ArrayList<>();
AtomicInteger counter = new AtomicInteger();
dates.forEach(d -> {

    int count = counter.getAndIncrement();
    if (count != 0) {

        Date previousDate = dates.get(count-1);
        long time = d.getTime()-previousDate.getTime();

        interArrivalTimes.add(time);
    }

});
double mean = interArrivalTimes.stream().mapToLong(i -> i).average().getAsDouble();

After I have obtained the list of time differences, I want to calculate the mean. But it is more about the looping part that I would like to shorten. Maybe something with reduction or collection?

Any help is greatly appreciated!

Tunaki
  • 132,869
  • 46
  • 340
  • 423
bashoogzaad
  • 4,611
  • 8
  • 40
  • 65
  • 2
    Almost always, when you refer to a `compareTo` method in a sort operation, like in your `.sorted((d1, d2) -> d1.compareTo(d2))`, you’re likely doing something obsolete. When your elements are `Comparable`, just use `sorted()` without arguments. – Holger Oct 29 '15 at 15:18
  • 1
    Thanks for you comment! I will keep this in mind. – bashoogzaad Oct 29 '15 at 15:19

1 Answers1

4

You could stream the indices instead of the dates:

IntStream.range(1, dates.size())
        .mapToLong(i -> dates.get(i).getTime() - dates.get(i - 1).getTime())
        .average().getAsDouble();

Or with a good old for loop:

LongStream.Builder b = LongStream.builder();
for (int i = 1; i < dates.size(); i++) {
  b.accept(dates.get(i).getTime() - dates.get(i - 1).getTime());
}
double mean = b.build().average().getAsDouble();

Or without streams:

double mean = 0;
for (int i = 1; i < dates.size(); i++) {
  mean += dates.get(i).getTime() - dates.get(i - 1).getTime();
}
if (!dates.isEmpty()) mean /= dates.size();
assylias
  • 321,522
  • 82
  • 660
  • 783
  • Thanks for your answer! That is a nice solution. Is it also possible to get the average directly from dates list by streaming and collecting it (just out of curiosity, because the code could become unreadable)? – bashoogzaad Oct 29 '15 at 14:55
  • 2
    @bashoogzaad You would probably have to build a custom collector such as described here: http://stackoverflow.com/a/30542049/829571 – assylias Oct 29 '15 at 14:58
  • 1
    Streams are amazing, but they are not the right tool for this job. A simple for-loop is really what you want. This is a common mistake. – Daniel Oct 29 '15 at 15:18