dreamer
dreamer

Reputation: 69

Java 8: Comparator comparingDouble type mismatch

final int[] brr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };

for (int i : brr) {
    System.out.println(Math.sin(i));
}

final int leastIdx = Arrays.stream(brr).min(Comparator.comparingDouble(i -> Math.sin(i))).get();

leastIdx should be returned as 4 but I can't get this to compile. Any insights appreciated.

The method min() in the type IntStream is not applicable for the 
arguments (Comparator.comparingDouble((<no type> i) -> Math.sin(i)))

Upvotes: 1

Views: 675

Answers (2)

Tagir Valeev
Tagir Valeev

Reputation: 100349

The proposed solutions have a flaw: they calculate the sine two times for almost every element. Thus for 1000 input elements you will have to calculate sine about 2000 times.

There are two solutions involving my StreamEx library which calculate the sine exactly once per stream element:

final int leastIdx = IntStreamEx
        .ofIndices(brr).boxed()
        .minByDouble(i -> Math.sin(brr[i])).get();

Or alternatively:

final int leastIdx = (int) IntStreamEx.of(brr)
        .mapToObj(Math::sin)
        .collect(MoreCollectors.minIndex()).getAsLong();

Both return 4 on your input. Unfortunately currently both of them involve boxing. Currently StreamEx does not provide a way to eliminate both boxing and twice sine calculation.

Upvotes: 2

Misha
Misha

Reputation: 28183

IntStream#min doesn't take a comparator; it just finds the smallest int in the stream. You have to either convert to Stream<Integer>:

Arrays.stream(brr).boxed()
        .min(comparingDouble(i -> Math.sin(i)))
        .get();

or calculate min using reduce:

Arrays.stream(brr)
        .reduce((x,y) -> Math.sin(x) > Math.sin(y) ? y : x )
        .getAsInt();

leastIdx should be returned as 4

Your stream is actually attempting to find the int with the smallest Math.sin, not the index of that int. If you want to find the index, you would need to stream over the indexes rather than over the values:

int leastIdx = IntStream.range(0, brr.length).boxed()
        .min(comparingDouble(i->Math.sin(brr[i])))
        .get();

or, if you are willing to sacrifice clarity to avoid boxing:

int leastIdx = IntStream.range(0, brr.length)
        .reduce((x,y) -> Math.sin(brr[x]) > Math.sin(brr[y]) ? y : x)
        .getAsInt();

Upvotes: 4

Related Questions