原始流与对象流和发生的实际装箱

Primitive stream vs object stream and actual boxing that occurs

所以我知道你可以有对象流,即 Stream<T> 和专业的原始流,例如IntStreamDoubleStream等。后者的好处之一是避免自动装箱。

此外,如果我们以 IntStream 为例,它有专门的操作,例如接受 IntPredicate 的过滤器。

我想知道我是 IntStream 还是 Stream<Integer>,在这一点上你节省了拳击费用,例如

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

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

在第一个示例中,我看不到任何装箱或拆箱操作。在第二个例子中,装箱/拆箱发生在哪里?因为如果流是 Stream<Integer> 并且过滤器接受 Predicate<Integer> 那么肯定不需要 box/unbox,IntConsumerConsumer<T>?

拆箱发生在谓词内部:在 stream.filter(x -> x >= 50) 中,Predicate 本质上变成了 (Integer x) -> x.intValue() >= 50,而 intValue() 是拆箱步骤。此外,System.out.println 在最终被调用的 Integer.toString 的实现中自己进行了一些拆箱操作。

Stream<Integer> 中,您的流已被装箱。因此,根据您创建它的方式,有几种可能性:

  • 您有最初的盒装值。例如,您将 List<Integer> list 作为输入并写道:

    stream = list.stream();
    
  • 您在创建流时将您的值装箱了。例如,您这样创建它:

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

    在这种情况下,第一个参数被装箱到 Integer.valueOf(1) 并且 unboxing/boxing 也会出现在每个 lambda(即 UnaryOperator<Integer>)调用中。如此有效,你有:

    stream = Stream.iterate(Integer.valueOf(1), x -> Integer.valueOf(x.intValue()+1));
    
  • 您通过上游中间操作明确地装箱了您的来源。例如:

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

    在这种情况下,装箱是在 .boxed() 操作内部执行的(这是 .mapToObj(Integer::valueOf) 的快捷方式)。