This question requires knowledge of autoboxing and unboxing, which can be CPU-intensive.

More quiz questions available here

Given the properly initialized stream

Stream<Integer> s = ... // initialize stream

How can you convert it to the stream of primitives? Choose one.

A. IntStream is = s.map(Function.identity());
B. IntStream is = s.mapToInt(Function.identity());
C. IntStream is = s.map(i -> i);
D. IntStream is = s.mapToInt(i -> i);

Answer. Inside the JVM, the handling of primitives is structurally different from the handling of reference types (objects). This difference spills over into the generics system, which handles only reference types.

To mix and match primitives with generics, you have to use the wrapper types (such as java.lang.Integer) along with autoboxing and unboxing. There is a downside with this approach, however, and that is that boxing and unboxing requires CPU effort. In extreme situations where a lot of primitive data is being handled, the impact on performance can be significant.

To mitigate this, Java provides four variations of the Stream type. There’s the regular object stream, which is simply called Stream, and there are three variations focused on handling primitives directly: IntStream, LongStream, and DoubleStream. Note that these types are all siblings; there is no assignment compatibility between them.

Any map(...) operation always returns a Stream of the same basic group. That is, an IntStream.map() operation returns an IntStream, and a Stream.map() returns another reference type, Stream (though the type of the data referenced can change).

Options A and C will fail because of this; they both call a simple map operation on a stream of reference type, and that will produce another stream of reference type (in both these cases, autoboxing will be applied and a Stream<Integer> is the result). Therefore, the result is not assignment compatible with IntStream. That’s why the code of those two options will not compile; therefore, options A and C are both incorrect.

To change the fundamental type of a stream, use a method that has a name that’s a variation of map. If you are changing to a stream of primitive int types, the method will be called mapToInt. If you are changing from one of the primitive stream types to a reference stream, the method will be called mapToObj.

If you provide the mapToInt operation with the lambda expression i -> i, as is done in option D, the compiler identifies that the functional interface to which this must be matched is a ToIntFunction<Integer>. From there, the compiler decides that the formal parameter i is of Integer type. The attempt to return an Integer when the function requires a primitive int simply means that unboxing should be applied. In other words, i -> i is translated to i -> i.intValue() to conform to the required return type. This code works as required, so option D is the correct answer.

Option D works, and the quiz question asks for only one answer, but let’s examine why option B fails. This is because the Function.identity() method creates an object that implements the interface Function<E, E>. In this context, E is determined to be Integer, and the expanded type is Function<Integer, Integer>. The compiler is faced with the generics system mandating that Function is not assignment compatible with ToIntFunction, and the code fails to compile. Therefore, option B is incorrect.

It’s worth mentioning that Function.identity() returns a singleton object, while i -> i creates a new instance each time it’s used. You can see this in action in the following example:

Function<Integer, Integer> iF1 = Function.identity();
Function<Integer, Integer> iF2 = Function.identity();
if (iF1 == iF2) System.out.println("Same");
Function<Integer, Integer> iF3 = i -> i;
Function<Integer, Integer> iF4 = i -> i;
if (iF3 != iF4) System.out.println("Not the same");

Here’s the output.

Same
Not the same

The singleton nature of Function.identity() holds even if the generic types of the functions are different (though you must trick the compiler to prove this in runtime).

Function<Integer, Integer> iF1 = Function.identity();
Function<String, String> iF2 = Function.identity();
Function f1 = iF1;
Function f2 = iF2;
if (f1 == f2) System.out.println("Same");

Here’s the output.

Same

Conclusion. The correct answer is option D.

Related quizzes