Why no OptionalDouble orElseNull() or a better solution

Viewed 267

I have a stream calculating average costs the code looks something like this

private Double calculateAverageCost(List<Item> items) {
    return items.stream()
                .mapToDouble(item -> item.cost)
                .filter(cost -> cost > 0) // Ignore zero cost items
                .average()
                . // Something here to convert an OptionalDouble to value or null
}

I need the method to return the value or null if there is no value (e.g. when all costs are zero). My problem is there is no orElseNull method on OptionalDouble to do the conversion. I can do it in another step e.g.

private Double calculateAverageCost(List<Item> items) {
    final OptionalDouble average = items.stream()
                .mapToDouble(item -> item.cost)
                .filter(cost -> cost > 0) // Ignore zero cost items
                .average();
    return average.isPresent() ? average.getAsDouble() : null;
}

I realise this is a "primitive" stream and my method is returning boxed Double but it would seem like this could be helpful, similar to (Optional.empty().orElseGet(null)).

Is there a reason or a better solution im missing?

2 Answers

OptionalDouble contains a primitive double. If you want an optional of a wrapped double you can just use Optional<Double>. Primitives can't be null, so orElseNull does not make sense.

As for how to convert between the two, it already has an answer here: Convert OptionalDouble to Optional <java.lang.Double>. TL;DR there is no nice, native way to do it yet. Wait for valhalla.

If you are returning null just to indicate that all costs are 0(or the list is empty), then you could return Double.NaN instead. This would allow your return type to be primitive double.

private double calculateAverageCost(List<Item> items) {
    return items.stream()
                .mapToDouble(item -> item.cost)
                .filter(cost -> cost > 0) // Ignore zero cost items
                .average()
                .orElse(Double.NaN);
}
double avg = calculateAverageCost(items);
if(Double.isNaN(avg)) {
    System.out.println("All costs are zero");
}

Otherwise, you could just use orElse(0d). Since you are filtering only positive costs, the average would be 0 only if all costs are 0 (or if the list is empty). So if the result is 0, then it definitely implies the absence of positive costs.

Related