qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請(qǐng)?jiān)L問(wèn) http://qaseven.github.io/

          Java 8的6個(gè)問(wèn)題

          1. 并行Streams實(shí)際上可能會(huì)降低你的性能
            Java8帶來(lái)了最讓人期待的新特性之–并行。parallelStream() 方法在集合和流上實(shí)現(xiàn)了并行。它將它們分解成子問(wèn)題,然后分配給不同的線程進(jìn)行處理,這些任務(wù)可以分給不同的CPU核心處理,完成后再合并到一起。實(shí)現(xiàn)原理主要是使用了fork/join框架。好吧,聽(tīng)起來(lái)很酷對(duì)吧!那一定可以在多核環(huán)境下使得操作大數(shù)據(jù)集合速度加快咯,對(duì)嗎?
            不,如果使用不正確的話實(shí)際上會(huì)使得你的代碼運(yùn)行的更慢。我們進(jìn)行了一些基準(zhǔn)測(cè)試,發(fā)現(xiàn)要慢15%,甚至可能更糟糕。假設(shè)我們已經(jīng)運(yùn)行了多個(gè)線程,然后使用.parallelStream() 來(lái)增加更多的線程到線程池中,這很容易就超過(guò)多核心CPU處理的上限,從而增加了上下文切換次數(shù),使得整體都變慢了。
            基準(zhǔn)測(cè)試將一個(gè)集合分成不同的組(主要/非主要的):
            Map<Boolean, List<Integer>> groupByPrimary = numbers
            .parallelStream().collect(Collectors.groupingBy(s -> Utility.isPrime(s)));
            使得性能降低也有可能是其他的原因。假如我們分成多個(gè)任務(wù)來(lái)處理,其中一個(gè)任務(wù)可能因?yàn)槟承┰蚴沟锰幚頃r(shí)間比其他的任務(wù)長(zhǎng)很多。.parallelStream() 將任務(wù)分解處理,可能要比作為一個(gè)完整的任務(wù)處理要慢。來(lái)看看這篇文章, Lukas Krecan給出的一些例子和代碼 。
            提醒:并行帶來(lái)了很多好處,但是同樣也會(huì)有一些其他的問(wèn)題需要考慮到。當(dāng)你已經(jīng)在多線程環(huán)境中運(yùn)行了,記住這點(diǎn),自己要熟悉背后的運(yùn)行機(jī)制。
            2. Lambda 表達(dá)式的缺點(diǎn)
            lambda表達(dá)式。哦,lambda表達(dá)式。沒(méi)有l(wèi)ambda表達(dá)式我們也能做到幾乎一切事情,但是lambda是那么的優(yōu)雅,擺脫了煩人的代碼,所以很容易就愛(ài)上lambda。比如說(shuō)早上起來(lái)我想遍歷世界杯的球員名單并且知道具體的人數(shù)(有趣的事實(shí):加起來(lái)有254個(gè))。
            List lengths = new ArrayList();
            for (String countries : Arrays.asList(args)) {
            lengths.add(check(country));
            }
            現(xiàn)在我們用一個(gè)漂亮的lambda表達(dá)式來(lái)實(shí)現(xiàn)同樣的功能:
            Stream lengths = countries.stream().map(countries -< check(country));
            哇塞!這真是超級(jí)厲害。增加一些像lambda表達(dá)式這樣的新元素到Java當(dāng)中,盡管看起來(lái)更像是一件好事,但是實(shí)際上卻是偏離了Java原本的規(guī)范。字節(jié)碼是完全面向?qū)ο蟮模殡S著lambda的加入 ,這使得實(shí)際的代碼與運(yùn)行時(shí)的字節(jié)碼結(jié)構(gòu)上差異變大。閱讀更多關(guān)于lambda表達(dá)式的負(fù)面影響可以看Tal Weiss這篇文章。
            從更深層次來(lái)看,你寫(xiě)什么代碼和調(diào)試什么代碼是兩碼事。堆棧跟蹤越來(lái)越大,使得難以調(diào)試代碼。一些很簡(jiǎn)單的事情譬如添加一個(gè)空字符串到list中,本來(lái)是這樣一個(gè)很短的堆棧跟蹤
            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表達(dá)式帶來(lái)的另一個(gè)問(wèn)題是關(guān)于重載:使用他們調(diào)用一個(gè)方法時(shí)會(huì)有一些傳參,這些參數(shù)可能是多種類型的,這樣會(huì)使得在某些情況下導(dǎo)致一些引起歧義的調(diào)用。Lukas Eder 用示例代碼進(jìn)行了說(shuō)明。
            提醒:要意識(shí)到這一點(diǎn),跟蹤有時(shí)候可能會(huì)很痛苦,但是這不足以讓我們遠(yuǎn)離寶貴的lambda表達(dá)式。
            3. Default方法令人分心
            Default方法允許一個(gè)功能接口中有一個(gè)默認(rèn)實(shí)現(xiàn),這無(wú)疑是Java8新特性中最酷的一個(gè),但是它與我們之前使用的方式有些沖突。那么既然如此,為什么要引入default方法呢?如果不引入呢?
            Defalut方法背后的主要?jiǎng)訖C(jī)是,如果我們要給現(xiàn)有的接口增加一個(gè)方法,我們可以不用重寫(xiě)實(shí)現(xiàn)來(lái)達(dá)到這個(gè)目的,并且使它與舊版本兼容。例如,拿這段來(lái)自Oracle Java教程中 添加指定一個(gè)時(shí)區(qū)功能的代碼來(lái)說(shuō):
          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));
          }
          }
            就是這樣,問(wèn)題迎刃而解了。是這樣么?Default方法將接口和實(shí)現(xiàn)分離混合了。似乎我們不用再糾結(jié)他們本身的分層結(jié)構(gòu)了,現(xiàn)在我們需要解決新的問(wèn)題了。想要了解更多,閱讀Oleg Shelajev在RebelLabs上發(fā)表的文章吧。
            提醒:當(dāng)你手上有一把錘子的時(shí)候,看什么都像是釘子。記住它們?cè)镜挠梅ǎ3衷瓉?lái)的接口而重構(gòu)引入新的抽象類是沒(méi)有意義的。
           4. 該如何拯救你,Jagsaw?
            Jigsaw項(xiàng)目的目標(biāo)是使Java模塊化,將JRE分拆成可以相互操作的組件。這背后最主要的動(dòng)機(jī)是渴望有一個(gè)更好、更快、更強(qiáng)大的Java嵌入式。我試圖避免提及“物聯(lián)網(wǎng)”,但我還是說(shuō)了。減少JAR的體積,改進(jìn)性能,增強(qiáng)安全性等等是這個(gè)雄心勃勃的項(xiàng)目所承諾的。
            但是,它在哪呢?Oracle的首席Java架構(gòu)師, Mark Reinhold說(shuō):  Jigsaw,通過(guò)了探索階段 ,最近才進(jìn)入第二階段,現(xiàn)在開(kāi)始進(jìn)行產(chǎn)品的設(shè)計(jì)與實(shí)現(xiàn)。該項(xiàng)目原本計(jì)劃在Java8完成。現(xiàn)在推遲到Java9,有可能成為其最主要的新特性。
            提醒:如果這正是你在等待的, Java9應(yīng)該在2016年間發(fā)布。同時(shí),想要密切關(guān)注甚至參與其中的話,你可以加入到這個(gè)郵件列表。
            5. 那些仍然存在的問(wèn)題
            受檢異常
            沒(méi)有人喜歡繁瑣的代碼,那也是為什么lambdas表達(dá)式那么受歡迎的的原因。想想討厭的異常,無(wú)論你是否需要在邏輯上catch或者要處理受檢異常,你都需要catch它們。即使有些永遠(yuǎn)也不會(huì)發(fā)生,像下面這個(gè)異常就是永遠(yuǎn)也不會(huì)發(fā)生的:
            try {
            httpConn.setRequestMethod("GET");
            }?catch (ProtocolException pe) { /* Why don’t you call me anymore? */ }
            原始類型
            它們依然還在,想要正確使用它們是一件很痛苦的事情。原始類型導(dǎo)致Java沒(méi)能夠成為一種純面向?qū)ο笳Z(yǔ)言,而移除它們對(duì)性能也沒(méi)有顯著的影響。順便提一句,新的JVM語(yǔ)言都沒(méi)有包含原始類型。
            運(yùn)算符重載
            James Gosling,Java之父,曾經(jīng)在接受采訪時(shí)說(shuō):“我拋棄運(yùn)算符重載是因?yàn)槲覀€(gè)人主觀的原因,因?yàn)樵贑++中我見(jiàn)過(guò)太多的人在濫用它。”有道理,但是很多人持不同的觀點(diǎn)。其他的JVM語(yǔ)言也提供這一功能,但是另一方面,它導(dǎo)致有些代碼像下面這樣:
            javascriptEntryPoints <<= (sourceDirectory in Compile)(base =>
            ((base / "assets" ** "*.js") --- (base / "assets" ** "_*")).get
            )
            事實(shí)上這行代碼來(lái)自Scala  Play框架,我現(xiàn)在都有點(diǎn)暈了。
            提醒:這些是真正的問(wèn)題么?我們都有自己的怪癖,而這些就是Java的怪癖。在未來(lái)的版本中可能有會(huì)發(fā)生一些意外,它將會(huì)改變,但向后兼容性等等使得它們現(xiàn)在還在使用。
            6. 函數(shù)式編程–為時(shí)尚早
            函數(shù)式編程出現(xiàn)在java之前,但是它相當(dāng)?shù)膶擂巍ava8在這方面有所改善例如lambdas等等。這是讓人受歡迎的,但卻不如早期所描繪的那樣變化巨大。肯定比Java7更優(yōu)雅,但是仍需要努力增加一些真正需要的功能。
            其中一個(gè)在這個(gè)問(wèn)題上最激烈的評(píng)論來(lái)自Pierre-yves Saumont,他寫(xiě)了一系列的文章詳細(xì)的講述了函數(shù)式編程規(guī)范和其在Java中實(shí)現(xiàn)的差異。
            所以,選擇Java還是Scala呢?Java采用現(xiàn)代函數(shù)范式是對(duì)使用多年Lambda的Scala的一種肯定。Lambdas讓我們覺(jué)得很迷惑,但是也有許多像traits,lazy evaluation和immutables等一些特性,使得它們相當(dāng)?shù)牟煌?/div>
            提醒:不要為lambdas分心,在Java8中使用函數(shù)式編程仍然是比較麻煩的。

          posted on 2014-12-03 13:36 順其自然EVO 閱讀(223) 評(píng)論(0)  編輯  收藏 所屬分類: 測(cè)試學(xué)習(xí)專欄

          <2014年12月>
          30123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          導(dǎo)航

          統(tǒng)計(jì)

          • 隨筆 - 3936
          • 文章 - 404
          • 評(píng)論 - 179
          • 引用 - 0

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 图木舒克市| 扎赉特旗| 中西区| 蓬莱市| 团风县| 抚松县| 镇康县| 罗城| 广昌县| 桂林市| 凤冈县| 厦门市| 丹阳市| 如东县| 鸡东县| 阜新市| 仙游县| 榆树市| 庆元县| 桐城市| 芜湖县| 新晃| 日照市| 友谊县| 海口市| 楚雄市| 青田县| 渝中区| 福州市| 永宁县| 揭东县| 苏州市| 阳西县| 甘德县| 伊宁县| 佳木斯市| 长宁区| 黑龙江省| 太和县| 调兵山市| 栖霞市|