4

I am trying to get closer to understanding streams thus I'm doing some basic exercises. In this one, I'd like to calculate the average of the odd numbers. I wrote this algorithm to do so, but it gives back an incorrect result (8.0). I've tried to debug it but I couldn't find what it does actually.

List<Integer> numbers = Arrays.asList(1, 3, -2, -4, -7, -3, -8, 12, 19, 6, 9, 10, 14);

OptionalDouble result = numbers.stream()
                               .filter(i -> i % 2 == 1)
                               .mapToDouble(i -> i).average();
if (result.isPresent()) {
   System.out.println(result);
} else {
   System.out.println("Error");
}

What is my code doing now? How should I fix it to do what it's supposed to do?

Naman
  • 27,789
  • 26
  • 218
  • 353
lyancsie
  • 658
  • 7
  • 18

3 Answers3

11
(i -> i % 2 == 1)

This is only true for positive odd numbers, because in Java the % operator returns a negative number (or zero) if its first operand is negative.

If you want to retain only even numbers, it should be:

(i -> i % 2 == 0)

If you want to retain all odd numbers (positive and negative), you can use:

(i -> i % 2 != 0)
khelwood
  • 55,782
  • 14
  • 81
  • 108
3

What is my code doing now?

You're performing a modulo operation on negative number.

How should I fix it to do what it's supposed to do?

You can use Math.abs to validate the absolute value is odd or not in your list:

OptionalDouble result = numbers.stream()
                .filter(i -> Math.abs(i) % 2 == 1) // verify id the absolute value is odd
                .mapToDouble(i->i).average();
Naman
  • 27,789
  • 26
  • 218
  • 353
2

In addition to what @khelwood mentioned regarding modulo with negative numbers.

It's important to know that, the filter intermediate operation does not remove elements nor does any stream operation, instead, filter returns a new stream in which all the elements satisfying the provided predicate i.e. i -> i % 2 == 1 are present.

i -> i % 2 == 1 is saying "only keep the element represented as i if it's an odd number".

if you want even then you should do i -> i % 2 == 0, reads as "only keep the element represented as i is it's an even number".


On another note, if you're running on JDK9 you can use ifPresentOrElse to simply the isPresent() check.

 numbers.stream()
        .filter(i -> i % 2 == 0)
        .mapToDouble(i -> i)
        .average()
        .ifPresentOrElse(System.out::println,
                        () -> System.out.println("Error"));
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126