Math.toIntExact inside lambda expression?

Viewed 240

I'm learning about lambda expressions. Given a list of names, I want to count the numbers of names that start with N.

I did that:

final static List<String> friends = Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott");

public static int countFriendsStartWithN() {
    return  Math.toIntExact(friends
            .stream()
            .filter(name -> name.startsWith("N"))
            .count());
}

The call to the count method returns a primitive long but I want an int.
I used Math.toIntExact to get the long value as int.

Is it possible to get the int value directly inside the lambda expression?

5 Answers

No, it is not possible to fit your call to toIntExact into your chain of method calls, your stream pipeline. This is because count is a terminal operation and returns a primitive long on which no method call is possible. A terminal operation is an operation that ends the stream pipeline and produces a result (or a side effect).

So I believe the best thing you can do is to live with the code you already have. IMHO it’s fine.

Well, here's a somewhat silly way of calculating the count as an int without casting:

public static int countFriendsStartWithN() {
    return friends.stream()
                  .filter(name -> name.startsWith("N"))
                  .mapToInt (s -> 1)
                  .sum();
}

You can't do anything inside the lambda expression you currently have, since that's a Predicate: it returns a boolean. Math.toIntExact returns an int.

You can do it without the Math.toIntExact (or a simple cast) like so:

return /* create stream, filter */
    .mapToInt(a -> 1).sum();

But this is likely to be slower than doing what you are doing at the moment.

Yet another option that is not really better - it is possible to use a collector that applies a finisher:

public static int countFriendsStartWithN() {
    return friends.stream()
                .filter(name -> name.startsWith("N"))
                .collect(Collectors.collectingAndThen(Collectors.counting(), Math::toIntExact));
}

This may have an advantage if you need it frequenty - you could build a utility method returning this Collector to make it reusable.

Here's a way to do this with reduce

public static int countFriendsStartWithN2() {
    return friends
            .stream()
            .filter(name -> name.startsWith("N"))
            .map(s -> 1)
            .reduce(0, Integer::sum);
}
Related