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