int colories=menu.stream()
.map(Dish::getCalories)
.reduce(0,Integer::sum);
这段有一个暗含的装箱成本。每个Integer都必须拆箱成一个原始类型,再进行求和。
map方法会生成一个Stream<T>
,虽然流中的元素是Integer类型,但Stream接口没有定义sum方法。为什么没有呢?比如,你只有一个像menu那样的Stream<Dish>
,把各种菜加起来是没有任何意义的。
Stream API提供了原始类型流特化,专门支持处理数值流的方法。
IntStream,DoubleStream和LongStream,分别将流中的元素特化为int,long和double,从而避免了暗含的装箱成本。每个接口都带来了进行常用数值归约的新方法,比如sum和max。
1.映射到数值流
mapToInt,mapToDouble和mapToLong。这些方法和前面说的map方法的工作方式一样,只是他们返回的是一个特化流,而不是Stream
int colories=menu.stream()
.mapToInt(Dish::getCalories)//返回一个IntStream
.sum();
如果流时空的,sum默认返回0。IntStream还支持其他的方法,如max、min、average等。
2.转换回对象流
IntStream intStream=menu.stream().mapToInt(Dish::getColories);//将Stream转换为数值流
Stream<Integer> stream=intStream.boxed();//将数值流转换为Stream
3.默认值OptionalInt
如果没有元素的话,求和的时候返回0,这没有问题。但如果要计算IntStream中的最大元素,就得换个法子了,因为0是错误结果。如何区分没有元素的流和最大值真的是0的流呢?
Optional可以用Integer、String等引用类型。对于三种原始流特化,也分别有一个Optional原始类型特化版本:OptionalInt、OptionalDouble和OptionalLong。
OptionalInt maxCalories=menu.stream()
.mapToInt(Dish::getColaries)
.max();
现在,如果没有最大值的话,可以显示处理OptionalInt去定义一个默认值了
int max=maxCalories.orElse(1);
数值范围
比如,假设你想要生成1和100之间的所有数字。Java8引入了两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:range和rangeClosed。这两个方法都是第一个参数接收起始值,第二个参数接受结束值。但range是不包含结束值的,而rangeClosed则包含结束值。
IntStream evenNumbers=IntStream.rangeClosed(1,100)
.filter(n->n%2==0);
System.out.println(evenNumbers.count());
这里用了rangeClosed方法来生成1到100之间的所有数字。它会产生一个流,然后你可以链接filter方法,只选出偶数。到目前为止还没有进行任何计算。最后,你对生成的流调用count。因为count是一个终端操作,所以它会处理流,并返回结果50,这正是1到100(包括两端)中所有偶数的个数。如果改用IntStream.range(1,100),则结果将会是49个偶数,因为range是不包含结束值的。