2018-09-21

java學(xué)習(xí)筆記(二)

前一篇簡(jiǎn)單的介紹了Java8函數(shù)式編程,這篇還將繼續(xù)函數(shù)式編程之旅。

在Java程序中制造和處理集合幾乎是必然的, 但是使用集合框架的效果并不理想. 例如: 你需要從列表中篩選出金額較高的交易, 然后按貨幣分組. 你可能需要寫一堆格式化的代碼:

Map<Currency, List<Transaction> > transactionByCurrencies = new HashMap<>();
for (Transaction transaction : transactions) {
    if (transaction.getPrise() > 1000) {
         Currency currency = transaction.getCurrency();
        List<Transaction> transactionForcurrency = transactionByCurrencies.get(currency);
        if ( transactionForcurrency = null) {
            transactionForcurrency = new ArrayList<>();
            transactionBycurrency.put(currency,  transactionForcurrency);
        }
        transactionForcurrency.add(transaction);
    }
}

我們很難一眼看出這些代碼做了什么, 嵌套的流程控制語(yǔ)句需要仔細(xì)地理解.

如果你使用Java8的Stream API, 你的代碼會(huì)簡(jiǎn)潔, 易于理解.

Map<Currency, List<Transaction> > transactionByCurrencies = transactions.stream()
    .filter((Transaction t) -> t.getPise() > 1000)
    .collect(groupingBy(Transaction::getCurrency));

就是這么簡(jiǎn)單, 數(shù)據(jù)的處理全部交由函數(shù)庫(kù)內(nèi)部進(jìn)行, 我們只需明確我們的動(dòng)作即可. 這種思想叫做內(nèi)部迭代, 和for-each的外部迭代是相對(duì)的.

初步了解了Stream, 你會(huì)發(fā)現(xiàn)這和Java以前的IO流是有很大的不同的, 雖然都是流. 但是很明顯Stream的流是更抽象的, 更高級(jí)的.

并行編程

所有新式的筆記本和臺(tái)式電腦都是多核的, 經(jīng)典的Java編程是單線程(只利用一核)模式, 這樣其他CPU資源就都浪費(fèi)了. 通過(guò)Java提供的多線程框架來(lái)實(shí)現(xiàn)并發(fā)編程并非易事, 傳統(tǒng)的synchronized, volatile關(guān)鍵字, Java5 之后提供的并發(fā)框架, 各種鎖的區(qū)別, 對(duì)于一個(gè)新人這是很不友好的. 沒(méi)有對(duì)Java內(nèi)存模型和Java虛擬機(jī)有一定的了解是很難編寫出正確的并發(fā)代碼的.

Java8的Stream API 提供的并行處理, 屏蔽了大量的底層細(xì)節(jié), 程序員只需簡(jiǎn)單的調(diào)用parallelStream方法即可達(dá)到并行處理. 可以稍微感受一下Java8并行的的簡(jiǎn)單, 利用并行從一個(gè)列表中選出比較重的蘋果.:


List<Apple> heavyApple = inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150).collect(toList());

你或許會(huì)好奇并行編程會(huì)啟動(dòng)多少個(gè)線程? 并行流內(nèi)部使用了默認(rèn)的ForkJoinPool, 默認(rèn)的線程數(shù)就是你的處理器個(gè)數(shù), 這個(gè)值是由Runtime.getRuntime().availableProcessors()得到. 這個(gè)大小是可以改變的, 不過(guò)對(duì)于并發(fā)編程, 數(shù)據(jù)密集型建議線程數(shù)等于處理器數(shù)或處理器數(shù)加一, IO密集型的建議線程數(shù)是兩倍與處理器數(shù)或加一.

默認(rèn)方法

Java8的默認(rèn)方法主要是為了支持程序庫(kù)設(shè)計(jì)人員, 讓他們寫出更容易改進(jìn)的接口. 這也是為了提高兼容性, 在Java8 引入函數(shù)式編程后, 在Java8 之前的的許多函數(shù)庫(kù)并沒(méi)有提供函數(shù)式的支持. 例如, 集合框架中就沒(méi)有stream方法, 但是你會(huì)發(fā)現(xiàn)之前的的代碼都是調(diào)用的集合框架的stream方法. 那是因?yàn)樵贑ollection<T> 接口中提供了默認(rèn)實(shí)現(xiàn).

如果沒(méi)有默認(rèn)方法, 為了添加一個(gè)方法, 你可能需要為所有實(shí)現(xiàn)這個(gè)接口的類提供一個(gè)方法的實(shí)現(xiàn), 并且實(shí)現(xiàn)的代碼都是同一套. 那簡(jiǎn)直是噩夢(mèng). 你可能忍不住想放棄添加這個(gè)方法. 因此Java8 就提供了默認(rèn)方法, 已解決擴(kuò)充接口可能帶來(lái)的既有代碼的破壞.

其他的函數(shù)式思想

常見(jiàn)的函數(shù)式語(yǔ)言, 如SML, OCaml, Haskell中還提供了其他的一些結(jié)構(gòu)來(lái)幫助程序員避免描述性數(shù)據(jù)的null引用問(wèn)題. 計(jì)算機(jī)科學(xué)巨擎之一 Tony Hoare 在2009年倫敦QCon上演講說(shuō)道:

我把它叫作我的 “ 價(jià)值億萬(wàn)美金的錯(cuò)誤 ” 。就是在1965年發(fā)明了空值引用.......我無(wú)法抗拒放進(jìn)一個(gè)空值引用的誘惑,僅僅是因?yàn)樗麑?shí)現(xiàn)起來(lái)非常容易。

Java8 提供了Optional<T> 類,它是幫助你避免出現(xiàn)NullPointerException 。這是個(gè)容器對(duì)象,可以包含或不包含一個(gè)值。只是一個(gè)空值引用的解決方案之一。

還有一種思想是模式匹配。舉一個(gè)簡(jiǎn)單的例子,比如你要寫一個(gè)Expr類型算術(shù)表達(dá)式的樹(shù)的基本化簡(jiǎn),使用Scala中你可以寫出這樣的代碼:

def simplifyExpression(expr: Expr): Expr = expr match {
    case BinOp("+", e, Number(0)) => e
    case BinOp("*", e, Number(1)) => e
    case BinOp("/", e, Number(1)) => e
    case _ => expr
}

Scala 語(yǔ)法里的expr match 和Java的switch差不多,不過(guò)Java的switch是不允許模式匹配的。或許你可以考慮使用Java的if-then-else來(lái)實(shí)現(xiàn)這個(gè)間的的程序,但那種方式更好,就屬于語(yǔ)言的爭(zhēng)論了。

這只是簡(jiǎn)單的介紹一下Java8 的函數(shù)式編程,受困與本人的表達(dá)能力有限,有不妥之處,還望指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容