Skip to content
Advertisement

Iterating over and reducing values of a Java map via Stream API

Java 8 here. I have an array of Strings:

String[] animals = getsomehow(); // "dogs", "cats", "sheep", etc.

Then I have a map where the keys are Strings (specifically, the same literal values as some of the animals in the array above), and the values are a count (representing the number of those animals):

Map<String,Integer> animalCounts = new HashMap<String,Integer>();
animalCounts.put("sheep", 4);
animalCounts.put("dogs", 2);
animalCounts.put("cats", 0);
animalCounts.put("porcupines", null);
animalCounts.put("dolphins", 43);

I am trying to figure out how to use the Stream API to iterate over my animals array, and come up with a total number of animals. For instance, if my animals array had “sheep” and “dolphins” in it, then the total number of animals would be 4 + 43 = 47.

My best attempt so far:

int totalAnimals = Arrays.stream(animals)
    .reduce(
        0,
        (subtotal, animal) -> subtotal + animalCounts.get(animal));

However this yields a compiler error for the identity value of 0:

Required type: String

Can anyone spot where I’m going awry?

Advertisement

Answer

Can anyone spot where I’m going awry?

You’re using the 2-argument version of reduce:

T reduce(T identity,
         BinaryOperator<T> accumulator)

As you can see, the identity value and the output has to be of the same type as the input, so it must be String.

Solution would be to use the 3-argument version of reduce:

<U> U reduce(U identity,
             BiFunction<U,? super T,U> accumulator,
             BinaryOperator<U> combiner)

As an alternative, you can do it like this:

int totalAnimals = Arrays.stream(animals)
        .map(animalCounts::get)
        .filter(Objects::nonNull)
        .mapToInt(Integer::intValue)
        .sum();
Advertisement