關於 Java 8 的6大頭疼問題
1. 並行Streams實際上可能會降低你的效能
Java8帶來了最讓人期待的新特性之–並行。parallelStream() 方法在集合和流上實現了並行。它將它們分解成子問題,然後分配給不同的執行緒進行處理,這些任務可以分給不同的CPU核心處理,完成後再合併到一起。實現原理主要是使用了fork/join框架。好吧,聽起來很酷對吧!那一定可以在多核環境下使得操作大資料集合速度加快咯,對嗎?
不,如果使用不正確的話實際上會使得你的程式碼執行的更慢。我們進行了一些基準測試,發現要慢15%,甚至可能更糟糕。假設我們已經執行了多個執行緒,然後使用.parallelStream() 來增加更多的執行緒到執行緒池中,這很容易就超過多核心CPU處理的上限,從而增加了上下文切換次數,使得整體都變慢了。
基準測試將一個集合分成不同的組(主要/非主要的):
Map<Boolean, List<Integer>> groupByPrimary = numbers .parallelStream().collect(Collectors.groupingBy(s -> Utility.isPrime(s)));
使得效能降低也有可能是其他的原因。假如我們分成多個任務來處理,其中一個任務可能因為某些原因使得處理時間比其他的任務長很多。.parallelStream() 將任務分解處理,可能要比作為一個完整的任務處理要慢。來看看這篇文章, Lukas Krecan給出的一些例子和程式碼 。
提醒:並行帶來了很多好處,但是同樣也會有一些其他的問題需要考慮到。當你已經在多執行緒環境中執行了,記住這點,自己要熟悉背後的執行機制。
2. Lambda 表示式的缺點
List lengths = new ArrayList(); for (String countries : Arrays.asList(args)) { lengths.add(check(country)); }
現在我們用一個漂亮的lambda表示式來實現同樣的功能:
Stream lengths = countries.stream().map(countries -< check(country));
哇塞!這真是超級厲害。增加一些像lambda表示式這樣的新元素到Java當中,儘管看起來更像是一件好事,但是實際上卻是偏離了Java原本的規範。位元組碼是完全物件導向的,伴隨著lambda的加入 ,這使得實際的程式碼與執行時的位元組碼結構上差異變大。閱讀更多關於lambda表示式的負面影響可以看Tal Weiss這篇文章。
從更深層次來看,你寫什麼程式碼和除錯什麼程式碼是兩碼事。堆疊跟蹤越來越大,使得難以除錯程式碼。一些很簡單的事情譬如新增一個空字串到list中,本來是這樣一個很短的堆疊跟蹤
at LmbdaMain.check(LmbdaMain.java:19) at LmbdaMain.main(LmbdaMain.java:34)
變成這樣:
at LmbdaMain.check(LmbdaMain.java:19) at LmbdaMain.lambda$0(LmbdaMain.java:37) at LmbdaMain$$Lambda$1/821270929.apply(Unknown Source) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.LongPipeline.reduce(LongPipeline.java:438) at java.util.stream.LongPipeline.sum(LongPipeline.java:396) at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526) at LmbdaMain.main(LmbdaMain.java:39
lambda表示式帶來的另一個問題是關於過載:使用他們呼叫一個方法時會有一些傳參,這些引數可能是多種型別的,這樣會使得在某些情況下導致一些引起歧義的呼叫。Lukas Eder 用示例程式碼進行了說明。
提醒:要意識到這一點,跟蹤有時候可能會很痛苦,但是這不足以讓我們遠離寶貴的lambda表示式。
3. Default方法令人分心
Default方法允許一個功能介面中有一個預設實現,這無疑是Java8新特性中最酷的一個,但是它與我們之前使用的方式有些衝突。那麼既然如此,為什麼要引入default方法呢?如果不引入呢?
Defalut方法背後的主要動機是,如果我們要給現有的介面增加一個方法,我們可以不用重寫實現來達到這個目的,並且使它與舊版本相容。例如,拿這段來自Oracle Java教程中新增指定一個時區功能的程式碼來說:
public interface TimeClient { // ... static public ZoneId getZoneId (String zoneString) { try { return ZoneId.of(zoneString); } catch (DateTimeException e) { System.err.println("Invalid time zone: " + zoneString + "; using default time zone instead."); return ZoneId.systemDefault(); } } default public ZonedDateTime getZonedDateTime(String zoneString) { return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString)); } }
就是這樣,問題迎刃而解了。是這樣麼?Default方法將介面和實現分離混合了。似乎我們不用再糾結他們本身的分層結構了,現在我們需要解決新的問題了。想要了解更多,閱讀Oleg Shelajev在RebelLabs上發表的文章吧。
4. 該如何拯救你,Jagsaw?
但是,它在哪呢?Oracle的首席Java架構師, Mark Reinhold說: Jigsaw,通過了探索階段 ,最近才進入第二階段,現在開始進行產品的設計與實現。該專案原本計劃在Java8完成。現在推遲到Java9,有可能成為其最主要的新特性。
提醒:如果這正是你在等待的, Java9應該在2016年間釋出。同時,想要密切關注甚至參與其中的話,你可以加入到這個郵件列表。
5. 那些仍然存在的問題
沒有人喜歡繁瑣的程式碼,那也是為什麼lambdas表示式那麼受歡迎的的原因。想想討厭的異常,無論你是否需要在邏輯上catch或者要處理受檢異常,你都需要catch它們。即使有些永遠也不會發生,像下面這個異常就是永遠也不會發生的:
try { httpConn.setRequestMethod("GET"); } catch (ProtocolException pe) { /* Why don’t you call me anymore? */ }
原始型別
它們依然還在,想要正確使用它們是一件很痛苦的事情。原始型別導致Java沒能夠成為一種純面嚮物件語言,而移除它們對效能也沒有顯著的影響。順便提一句,新的JVM語言都沒有包含原始型別。
運算子過載
James Gosling,Java之父,曾經在接受採訪時說:“我拋棄運算子過載是因為我個人主觀的原因,因為在C++中我見過太多的人在濫用它。”有道理,但是很多人持不同的觀點。其他的JVM語言也提供這一功能,但是另一方面,它導致有些程式碼像下面這樣:
javascriptEntryPoints <<= (sourceDirectory in Compile)(base => ((base / "assets" ** "*.js") --- (base / "assets" ** "_*")).get )
事實上這行程式碼來自Scala Play框架,我現在都有點暈了。
提醒:這些是真正的問題麼?我們都有自己的怪癖,而這些就是Java的怪癖。在未來的版本中可能有會發生一些意外,它將會改變,但向後相容性等等使得它們現在還在使用。
6. 函數語言程式設計–為時尚早
函數語言程式設計出現在java之前,但是它相當的尷尬。Java8在這方面有所改善例如lambdas等等。這是讓人受歡迎的,但卻不如早期所描繪的那樣變化巨大。肯定比Java7更優雅,但是仍需要努力增加一些真正需要的功能。
其中一個在這個問題上最激烈的評論來自Pierre-yves Saumont,他寫了一系列的文章詳細的講述了函數語言程式設計規範和其在Java中實現的差異。
所以,選擇Java還是Scala呢?Java採用現代函式正規化是對使用多年Lambda的Scala的一種肯定。Lambdas讓我們覺得很迷惑,但是也有許多像traits,lazy evaluation和immutables等一些特性,使得它們相當的不同。
提醒:不要為lambdas分心,在Java8中使用函數語言程式設計仍然是比較麻煩的。
相關文章
- 最令人頭疼的Python問題Python
- 頭疼,大事務問題如何解決?
- 關於java的“原子操作”問題Java
- 關於介面返回BOM頭處理的問題
- 一個關於Java Excel的問題JavaExcel
- 關於QT的標頭檔案相互包含的問題QT
- 問一個關於oracle8的簡單的問題!Oracle
- Java春招面試複習:有關於Java Map,應該掌握的8個問題Java面試
- java中關於Map的九大問題Java
- 關於Java編碼規範的問題Java
- 請教大家關於java效能的問題Java
- 關於java吃記憶體的問題Java記憶體
- PLM與ERP整合,這個頭疼的問題,可以這樣解決!
- 一個關於java.net.URL的問題.Java
- 關於Java NIO的一些問題,求助。Java
- 剛學java,關於介面的問題Java
- 「IT運維迷宮」那些讓人頭疼的常見問題與破局之道運維
- 關於SQLServerDriver的問題SQLServer
- 關於 JavaMail 的問題JavaAI
- 關於session的問題Session
- Java中關於二分查詢的問題Java
- Java關於初始化問題的總結(一)Java
- 關於Java communications API的問題,請教高手JavaAPI
- 關於Java中文問題的幾條分析原則Java
- BothWin分包與熱更新:安裝與升級,開發者頭疼的問題在此破局
- 小小問題―關於java多執行緒Java執行緒
- 關於學習java中的按位取反(~)的問題Java
- 頭疼的null值,自敬彬Null
- Leetcode刷題中關於java的一些小問題LeetCodeJava
- 關於Java序列化的問題你真的會嗎?Java
- 關於Java異常最常見的八大問題Java
- 關於Java中分層中遇到的一些問題Java
- Java中關於String型別的10個問題Java型別
- java web中關於修改xml後讀取值的問題JavaWebXML
- 關於javascript的this指向問題JavaScript
- 關於跨域的問題跨域
- 關於bit code的問題
- 關於序列同步的問題