开销:在原始流与拳击之间进行转换

时间:2017-08-25 11:03:11

标签: java java-8 java-stream primitive-types

我正在使用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(...)吗?

2 个答案:

答案 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