Why can't I map integers to strings when streaming from an array?
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.
Solution 3
You can create an Integer Stream using Arrays.stream(int[]) , you can call mapToObj
like mapToObj(Integer::toString)
.
String csn = Arrays.stream(numbers) // your numbers array
.mapToObj(Integer::toString)
.collect(Collectors.joining(", "));
Hope this helps..
Solution 4
No boxing, AFAIK, and no explosion of little strings added to the heap:
public static void main(String[] args) {
IntStream stream = IntStream.of(1, 2, 3, 4, 5, 6);
String s = stream.collect(StringBuilder::new, (builder, n) -> builder.append(',').append(n), (x, y) -> x.append(',').append(y)).substring(1);
System.out.println(s);
}
Solution 5
If the purpose of this sample and question is to figure out how to map strings to a stream of ints (for example, using a stream of ints to access an index in an Array of strings), you can also use boxing, then casting to an int (which would then allow accessing the index of the array).
int[] numbers = {0, 1, 2, 3};
String commaSeparatedNumbers = Arrays.stream(numbers)
.boxed()
.map((Integer i) -> Integer.toString((int)i))
.collect(Collectors.joining(", "));
The .boxed() call converts your IntStream (a stream of primitive ints) to a Stream (a stream of objects -- namely, Integer objects) which will then accept the return of an object (in this case, a String object) from your lambda. Here it is just a string representation of the number for demonstration purposes, but it could just as easily (and more practically) be any string object -- like the element of a string array as mentioned before.
Just thought I'd offer another possibility. In programming, there are always multiple ways of accomplishing a task. Know as many as you can, then choose the one that fits the best for the task at hand, keeping in mind performance issues, intuitiveness, clarity of code, your preferences in coding style, and the most self-documenting.
Happy coding!
Related videos on Youtube
Denys Séguret
Author of several popular open-source programs and libraries. Also known as dystroy @ Miaou or Canop @ GitHub. I'm also available as a freelance programmer and consultant to solve your problems or design your next system. My current focus is Rust but I have a wide full-stack experience. Contact information on https://dystroy.org
Updated on April 01, 2022Comments
-
Denys Séguret about 2 years
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 ?
-
Florian Margaine about 9 yearsWhat's the difference between
IntStream
andStream<Integer>
? -
Alexis C. about 9 years@FlorianMargaine An
IntStream
is a stream specialization for primitiveint
values. AStream<Integer>
is just a Stream holdingInteger
objects. -
assylias about 9 years@FlorianMargaine
IntStream
is a stream or primitives (ints) whereasSteram<Integer>
is a stream of objects. Primitive streams have specialised methods for performance reasons. -
Holger about 9 years
IntStream.mapToObj
expects anIntFunction
, a function which consumes anint
value, therefore.mapToObj((Integer i) -> i.toString())
does not work. It wouldn’t be recommended anyway as it contains an unnecessary conversion fromint
toInteger
. In contrast,.mapToObj(Integer::toString)
works nicely as it will call thestatic
methodInteger.toString(int)
. Note that this is different to calling.map(Integer::toString)
on aStream<Integer>
as the latter won’t compile because it is ambiguous. -
cic about 9 years@Holger: Regarding, the
Stream<Integer>
case. Isn't that simply a bad error message (i.e., the reference being ambiguous), as the real error is a type mismatch? -
Holger about 9 years@cic: no, calling
.map(Integer::toString)
on aStream<Integer>
is truly ambiguous as both,.map(i->i.toString())
and.map(i->Integer.toString(i))
are valid. But it’s easily solvable by using.map(Object::toString)
. -
Josh Glover almost 9 yearsThanks for introducing me to the
Class::staticMethod
syntax sugar! That's very nice indeed. -
Alexis C. over 8 yearsYou're doing unnecessary work. You box each
int
to its wrapper typeInteger
and you unbox it just after.