java里函数式表达式_Java8函数式编程 (一) 数据流和lambda表达式
JDK 1.8中引入了函數(shù)式編程(functional programming,FP),如果您已習(xí)慣OOP,一定會感到困惑:什么是函數(shù)式編程?這樣的編程模式有什么好處?
本文將通過簡單的實(shí)例令讀者對函數(shù)式編程有一個大體的了解。
我們知道OOP是以類為基礎(chǔ)的,程序中必須首先抽象和定義class。那么FP創(chuàng)建的基礎(chǔ)是什么?或者說在Java 8中,至少需要了解什么知識點(diǎn)才能實(shí)現(xiàn)基本的函數(shù)式編程呢?
本文將首先介紹在Java 8中使用FP所需的基本知識點(diǎn):Lambda表達(dá)式
數(shù)據(jù)流
基本實(shí)例Map>?phoneNumbers?=?new?HashMap>();phoneNumbers.put("Zhang?Jin",?Arrays.asList("3232312323",?"8933555472"));phoneNumbers.put("Li?Ming",?Arrays.asList("12323344",?"492648333"));phoneNumbers.put("Li?Guoping",?Arrays.asList("77323344",?"938448333"));Map>?filteredNumbers?=?phoneNumbers.entrySet().stream().filter(x?->?x.getKey().contains("Li")).collect(Collectors.toMap(p?->?p.getKey(),?p?->?p.getValue()));filteredNumbers.forEach((key,?value)?->?{System.out.println("Name:?"?+?key?+?":?");value.forEach(System.out::println);});
上半部分的代碼創(chuàng)建了一個從人名到此人所有電話號碼的Map,比較簡單,但接下來的部分對剛接觸Java 8的讀者不是一眼就能理解,我們一會兒來詳細(xì)解釋一下。
數(shù)據(jù)狀態(tài)不變原則
在分析上面的code前,先來看看FP與我們所熟知的OOP的最大不同。
在面向?qū)ο缶幊虝r,我們定義類和對象,并使用方法或表達(dá)式來執(zhí)行命令。這些方命令通常會改變程序的數(shù)據(jù)狀態(tài):Integer x = 0;x++;
比如上面的代碼,當(dāng)執(zhí)行x++后,x的值產(chǎn)生了變化,舊數(shù)據(jù)被新數(shù)據(jù)所取代。
相反,在函數(shù)式編程中,盡管我們也需要通過方法來執(zhí)行命令,但命令本身并不會改變程序已有的數(shù)據(jù)。簡單地說就是函數(shù)調(diào)用對外界不產(chǎn)生副作用,并總是輸出新的變量作為調(diào)用結(jié)果,比如下面的javascript:function max(a, b) {return a > b ? a : b;}var x = 10;var y = 5;var maximum = max(x, y);
此處max并不改變和依賴于外部的x和y,同時每次返回的結(jié)果都是一個全新的變量。
代碼分析
現(xiàn)在我們回到之前的代碼:// create a map, filter it by key valuesMap > filteredNumbers = phoneNumbers.entrySet().stream().filter(x -> x.getKey().contains("Li")).collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
這段代碼首先調(diào)用entrySet()來獲得phoneNumbers的entries集合,其中每個entry都由一個鍵值對組成。
接著,在得到的集合上調(diào)用stream()方法,該方法會創(chuàng)建一個數(shù)據(jù)流 (java.util.stream.Stream)。數(shù)據(jù)流(stream)是Java 8引入的新概念,參考Javadoc,可以將它理解為是一種可以在其上執(zhí)行順序或并行聚合操作的數(shù)據(jù)序列。這些操作可以包括過濾,修改,不同類型的轉(zhuǎn)換等。
得到stream后,代碼調(diào)用了filter(),filter通過給定的條件來過濾數(shù)據(jù)。此處的過濾條件是:x?->?x.getKey().contains("Li")
該處采用了Java 8引入的lambda表達(dá)式,我們可以把lambda表達(dá)式看成一個簡潔的匿名函數(shù),->左邊的x是輸入?yún)?shù),->右邊是需要執(zhí)行的命令。這里的lambda表達(dá)式又被稱作predicate,它是一個返回boolean類型的函數(shù),stream中的每個element都會被代入該predicate,通過運(yùn)算出的結(jié)果來決定是否在filter()重新返回的stream中被留下或移除。
下一個方法是collect(),可以看到,這里的方法調(diào)用都是鏈?zhǔn)降?#xff0c;原因在于這些方法都?xì)w屬于Stream接口,所以使用起來非常方便簡潔。
collect()方法的用途很簡單,它先從stream內(nèi)獲得數(shù)據(jù)(這里是java.util.Map.Entry),再將數(shù)據(jù)轉(zhuǎn)變成通常的java collection,比如List, Map, Set等數(shù)據(jù)結(jié)構(gòu)。這樣就重新將stream轉(zhuǎn)變成了我們所熟悉的類型。
Collection的遍歷
來看最后一部分代碼:filteredNumbers.forEach((key, value) -> {System.out.println("Name: " + key + ": ");value.stream().forEach(System.out::println);});
該代碼打印出所有的結(jié)果,但并沒有使用循環(huán),而是用了Java 8引入的forEach方法。
有了前面的基礎(chǔ),可以很容易看懂:forEach接收一個lambda表達(dá)式,filteredNumbers的每個element都講執(zhí)行該表達(dá)式。由于filteredNumbers是一個map,所以這里lambda表達(dá)式參數(shù)變成了key, value pair:// expression takes two parameters(key, value) - > {// print person’s nameSystem.out.println("Name: " + key + ": ");// iterate over the person’s phone numbers and print each of themvalue.forEach(System.out::println);}
由于value本身是一個List,所以又在其上調(diào)用了forEach。
至此代碼的輸出為:Name: Li Ming:12323344492648333Name: Li Guoping:77323344938448333
學(xué)到的知識點(diǎn) (以及問題)
本文表述的幾個關(guān)鍵點(diǎn):Java 8通過數(shù)據(jù)流和lambda表達(dá)式使函數(shù)式編程成為可能。
Lambda表達(dá)式和匿名函數(shù)十分相似。
Stream是一種可以在其上執(zhí)行順序或并行聚合操作的數(shù)據(jù)序列,這些操作會作用于序列里的每個元素。
函數(shù)式編程模式中的操作傾向于不依賴和不改變已有的數(shù)據(jù)狀態(tài)。
數(shù)據(jù)流操作很簡潔性 — 如果不考慮執(zhí)行效率的話。
但現(xiàn)在您可能會想:函數(shù)式編程寫出來的東西理解起來似乎更困難 — 我還是喜歡原來的寫法, 盡管它要啰嗦很多。
streams操作的執(zhí)行效率高嗎?
如果不改變數(shù)據(jù)狀態(tài),程序應(yīng)該怎么寫才好呢?
這些問題我會在后續(xù)的章節(jié)中進(jìn)一步討論,盡情期待。
本文出自 “Bug之家” 博客,轉(zhuǎn)載請與作者聯(lián)系!
總結(jié)
以上是生活随笔為你收集整理的java里函数式表达式_Java8函数式编程 (一) 数据流和lambda表达式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql40题_mysql40题
- 下一篇: php缓存变量_PHP 从缓存中取出存储