2017-08-25 73 views
2

我使用Java 8个流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(", ")); 
} 

问题

  • 我假设有头顶物体之间切换时/原始流类型...如果我坚持流<>,这与拳击开销相比如何?

  • 如果我后来改回来,怎么办?

具体:
在分析师,我应该使用.map(...).mapToDouble(...)
在解释,我应该使用.mapToLong(...).mapToObj(...)

+3

我还有一个问题需要补充:在系统中它有多重要?你有没有体验/期望这方面的任何性能问题? “我应该考虑只用双倍/双倍” - 您的业务需求建议如何?你的模型是什么样的? – Thomas

+1

如果这是您希望改进的工作代码,该问题可能会在[CodeReview SE](https://codereview.stackexchange.com/)上吸引更多有趣的答案。然而,它可能需要返工以包含您简化的部分以及相关类的定义。 – Aaron

+0

@Thomas - 我现在对表演相当舒服(这足以让我看重可读性)。我想我要求更多的了解是否在流的中间部分正确使用mapToLong()和map()。我会重复双倍/双倍......这太开放了。 – AjahnCharles

回答

3

让我们打破这:

.mapToLong(this::getWeekFrequency) 

给你一个原始长。

.mapToDouble(process::apply) 

这基本long因为process功能需要被装箱到Longprocess返回映射到基元double的Float(通过Float.doubleValue())。

这些总结和总和被投给原始浮动(缩小,但你说安全),然后返回。


那么我们该如何摆脱一些自动装箱?我们想要一个FunctionalInterface,它完全符合我们的process函数,而不使用任何盒类。没有一个我们可以使用现成的架子,但我们可以很容易地定义它,像这样:

@FunctionalInterface 
public interface LongToFloatFunction 
{ 
    float apply(long l); 
} 

然后我们改变我们的声明:

private LongToFloatFunction process; 

,并保持一切相同这将阻止任何自动装箱。函数返回的原始float将自动扩展为原始的double。

+0

谢谢迈克尔;这真的有助于加入我理解的点(特别是更好地意识到原始的'Function'类型,并且我可以创建自己的原语 - >原语'FunctionalInterface's,其中没有框架实现存在) – AjahnCharles

+0

在我的完整上下文中,我的进程函数有一些通用的逻辑独立于输入类型(即我可以将Float更改为浮点型,但我可能希望保持输入参数装箱:'float apply(T t)'。在之前的编辑中,您提到了OOTB' ToDoubleFunction'这可能更适合我(虽然也许我会做我自己的ToFloatFunction)。假设输入将始终装入函数的输入框中,这是否意味着'.map(myToFloatFunction).mapToDouble(process: :apply)'更有效率?(因为无论如何都是长方形的,并且避免改变Stream类型) – AjahnCharles

+1

对于大多数实际的目的,没有理由将这个LongToFloatFunction作为已经存在的LongToDoubleFunction足够。对于中间值,很少有理由将其减少到“float”值集,使用“float”仅用于*存储*。最好的例子是根本没有'FloatStream',因为*处理*管道没有使用'float'而不是'double'。整齐的副作用是,您可以直接将函数传递给'LongStream.mapToDouble',而无需通过':: apply'对其进行调整。 – Holger

2

那么它从你的定义似乎process看起来有点像这样:

double process (long value) { 
     // do something 
} 

因此,如果你这样做:map(...).mapToDouble你会被创建每次型Long的对象,只有拆箱后立即用于process。我会离开代码,因为它是使用原始实现来避免这种情况。第二个使用String#valueOf。在long的情况下,String.valueOf(l)将被调用,其在原语上工作:Long.toString(l)

Object的情况下,将调用相同的方法,与第一次拳击发生的警告。所以,我会将其更改为mapToLong

+0

+1:谢谢你的回答@Eugene;它帮助我更详细地了解内部步骤。我和迈克尔的回答一起去了,因为对我的解决方案重要的是增加了对原始函数和函数接口的理解。 – AjahnCharles