Search code examples
javajava-streamprimitiveautoboxing

Primitive stream vs object stream and actual boxing that occurs


So I understand you can have object streams, i.e. Stream<T> and specialist primitive streams, e.g. IntStream, DoubleStream, etc. One of the benefits of the latter is to avoid autoboxing.

Also if we take IntStream as an example, it has specialised operations such as filter that accepts an IntPredicate.

I wondered if I had an IntStream vs a Stream<Integer> at which point you're saving on boxing, e.g.

intstream.filter(x -> x >= 50).forEach(x -> System.out.println(x));

vs

stream.filter(x -> x >= 50).forEach(x -> System.out.println(x));

In the first example, there is no boxing or unboxing that I can see. In the second example where is the boxing / unboxing occurring? Because if stream is a Stream<Integer> and the filter accepts a Predicate<Integer> then surely there's no need to box/unbox, and the same for IntConsumer vs Consumer<T>?


Solution

  • With Stream<Integer> your stream is already boxed. So depending on how you created it there are several possibilities:

    • You have initially boxed values. For example, you had List<Integer> list as the input and wrote:

      stream = list.stream();
      
    • You boxed your values when creating the stream. For example, you created it like this:

      stream = Stream.iterate(1, x -> x+1);
      

      In this case the first argument is boxed to Integer.valueOf(1) and unboxing/boxing also occurs on every lambda (which is UnaryOperator<Integer>) invocation. So effectively you have:

      stream = Stream.iterate(Integer.valueOf(1), x -> Integer.valueOf(x.intValue()+1));
      
    • You boxed your source explicitly via upstream intermediate operation. For example:

      stream = IntStream.range(0, 1000).boxed();
      

      In this case boxing is performed inside the .boxed() operation (which is a shortcut for .mapToObj(Integer::valueOf)).