Why can't I map integers to strings when streaming from an array?

This code works (taken in the Javadoc):

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
String commaSeparatedNumbers = numbers.stream()
    .map(i -> i.toString())
    .collect(Collectors.joining(", "));

This one can't be compiled:

int[] numbers = {1, 2, 3, 4};
String commaSeparatedNumbers = Arrays.stream(numbers)
    .map((Integer i) -> i.toString())
    .collect(Collectors.joining(", "));

IDEA tells me I have an "incompatible return type String in lambda expression".

Why ? And how to fix that ?


Solution 1:

Arrays.stream(int[]) creates an IntStream, not a Stream<Integer>. So you need to call mapToObj instead of just map, when mapping an int to an object.

This should work as expected:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(i -> ((Integer) i).toString()) //i is an int, not an Integer
    .collect(Collectors.joining(", "));

which you can also write:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(Integer::toString)
    .collect(Collectors.joining(", "));

Solution 2:

Arrays.stream(numbers) creates an IntStream under the hood and the map operation on an IntStream requires an IntUnaryOperator (i.e a function int -> int). The mapping function you want to apply does not respect this contract and hence the compilation error.

You would need to call boxed() before in order to get a Stream<Integer> (this is what Arrays.asList(...).stream() returns). Then just call map as you did in the first snippet.

Note that if you need boxed() followed by map you probably want to use mapToObj directly.

The advantage is that mapToObj doesn't require to box each int value to an Integer object; depending on the mapping function you apply of course; so I would go with this option which is also shorter to write.