我正在使用Java 8 Stream API,如下所示:
private Function<Long, Float> process; // Intermediate step (& types)
private long getWeekFrequency(final ScheduleWeek week) {
return week.doStreamStuff().count(); // Stream<>.count() returns long
}
@Override
public float analyse(final Schedule sample) {
return (float) sample // cast back to float
.getWeeks()
.stream()
.mapToLong(this::getWeekFrequency) // object to long
.mapToDouble(process::apply) // widen float to double
.sum();
}
@Override
public String explain(final Schedule sample) {
return sample
.getWeeks()
.stream()
.map(this::getWeekFrequency) // change stream type?
.map(String::valueOf)
.collect(Collectors.joining(", "));
}
我认为在对象/原语流类型之间进行更改时会产生开销...如果我坚持使用Stream&lt;&gt;,这与拳击开销相比如何?
如果我稍后改回来怎么办?
具体地:
在分析师中,我应该使用.map(...).mapToDouble(...)
吗?
在解释中,我应该使用.mapToLong(...).mapToObj(...)
吗?
答案 0 :(得分:3)
所以让我们打破这个:
.mapToLong(this::getWeekFrequency)
给你一个原始的长。
.mapToDouble(process::apply)
这个原始long被加到Long
,因为process
函数需要它。 process
返回Float
,映射到原始双精度(通过Float.doubleValue()
)。
将它们相加,并将总和转换为原始浮点数(缩小,但你说安全),然后返回。
那么我们怎样才能摆脱一些自动装箱?我们希望FunctionalInterface
与我们的process
函数完全匹配,而不使用任何框类。没有一个我们可以使用现成的,但我们可以很容易地定义它:
@FunctionalInterface
public interface LongToFloatFunction
{
float apply(long l);
}
然后我们将声明更改为:
private LongToFloatFunction process;
并保持其他所有内容相同,这将阻止任何自动装箱。函数返回的原始float将自动扩展为原始double。
答案 1 :(得分:2)
从你的定义看来process
看起来有点像这样:
double process (long value) {
// do something
}
如果您这样做:map(...).mapToDouble
,您每次都会创建一个Long
类型的对象,只有在process
中使用后立即将其取消装箱。我会保留代码,因为它是使用原始实现来避免这种情况。
第二个使用String#valueOf
。如果是long
,则会调用String.valueOf(l)
,它适用于基元:Long.toString(l)
。
如果Object
将调用相同的方法,但是,需要注意第一次拳击发生。所以,我会将其更改为mapToLong