2017-08-25 71 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