⑴ 函數式編程-Lambda與Stream
我們在創建線程並啟動時可以使用匿名內部類的寫法:
可以使用Lambda的格式對其進行修改。修改後如下:
現有方法定義如下,其中IntBinaryOperator是一個介面。先使用匿名內部類的寫法調用該方法。
Lambda寫法:
現有方法定義如下,其中IntPredicate是一個介面。先使用匿名內部類的寫法調用該方法。
Lambda寫法:
現有方法定義如下,其中Function是一個介面。先使用匿名內部類的寫法調用卜行握該方法。
Lambda寫法:
現有方法定義如下,其中IntConsumer是一個介面。先使用匿名內部類的寫法調用該方法。
Lambda寫法:
java8的Stream使用的是函數式編程模式,如同它的名字一樣,它可以被用來對集合或數組進行鏈狀流式的操作。可以更方便的讓我們對集合或數組操作。
我們可以調用getAuthors方法獲取到作家的集合。現在需要列印所有年齡小於18的作家的名字,並且要注意去重。
單列集合: 集合對象.stream()
數組:Arrays.stream(數組)或者使用Stream.of來創建
雙列集合:轉換成單列集合後再創建
可以對流中的元素進行條件過濾,符合過濾條件的才能繼續留在流中。
例如:
列印所有姓名長度大於1的作家的姓名
可以把對流中的元素進行計算或轉換。
例如:
列印所有作家的姓名
可以去除流中的重復元素。
例如:
列印所有作家的姓名,並且要求其中不能有重復元素。
注意:distinct方法是依賴Object的equals方法來判斷是否是相同對象的。所以需要注意重寫equals方法。
可以對流中的元素進行排序。
例如:
對流中的元素按照年齡進行降序排序,並且要求不能有重復的元素。
注意:如果調用空參的sorted()方法,需要流中的元素是實現了Comparable。
可以設置流的最大長度,超出的部分將被拋棄。帶昌
例如:
對流中的元素按照年齡進行降序排序,並且要求不能有重復的元素,然後列印其中年齡最大的兩個作家的姓名。
跳過流中的前n個元素,返回剩下的元素
例如:
列印除了年齡最大的作家外的其他作家,要求不能有重復元素,並且按照年齡降序排序。
map只能把一個對象轉換成另一個對象來作為流中的元素。而flatMap可以把一個對象轉換成多個對象作為流中的元素。
例一:
列印所有書籍的名字。要求對重復的元素進行去重。
例二:
列印現有數據的所有分類。要求對分類進行去重。不能出現這種格式:哲學,愛情
對流中的元素進行遍歷操作,我們通過傳入的型慶參數去指定對遍歷到的元素進行什麼具體操作。
例子:
輸出所有作家的名字
可以用來獲取當前流中元素的個數。
例子:
列印這些作家的所出書籍的數目,注意刪除重復元素。
可以用來或者流中的最值。
例子:
分別獲取這些作家的所出書籍的最高分和最低分並列印。
把當前流轉換成一個集合。
例子:
獲取一個存放所有作者名字的List集合。
獲取一個所有書名的Set集合。
獲取一個Map集合,map的key為作者名,value為List
可以用來判斷是否有任意符合匹配條件的元素,結果為boolean類型。
例子:
判斷是否有年齡在29以上的作家
可以用來判斷是否都符合匹配條件,結果為boolean類型。如果都符合結果為true,否則結果為false。
例子:
判斷是否所有的作家都是成年人
可以判斷流中的元素是否都不符合匹配條件。如果都不符合結果為true,否則結果為false
例子:
判斷作家是否都沒有超過100歲的。
獲取流中的任意一個元素。該方法沒有辦法保證獲取的一定是流中的第一個元素。
例子:
獲取任意一個年齡大於18的作家,如果存在就輸出他的名字
獲取流中的第一個元素。
例子:
獲取一個年齡最小的作家,並輸出他的姓名。
對流中的數據按照你指定的計算方式計算出一個結果。(縮減操作)
rece的作用是把stream中的元素給組合起來,我們可以傳入一個初始值,它會按照我們的計算方式依次拿流中的元素和初始化值進行計算,計算結果再和後面的元素計算。
rece兩個參數的重載形式內部的計算方式如下:
其中identity就是我們可以通過方法參數傳入的初始值,accumulator的apply具體進行什麼計算也是我們通過方法參數來確定的。
例子:
使用rece求所有作者年齡的和
使用rece求所有作者中年齡的最大值
使用rece求所有作者中年齡的最小值
rece一個參數的重載形式內部的計算
如果用一個參數的重載方法去求最小值代碼如下:
我們在編寫代碼的時候出現最多的就是空指針異常。所以在很多情況下我們需要做各種非空的判斷。
例如:
尤其是對象中的屬性還是一個對象的情況下。這種判斷會更多。
而過多的判斷語句會讓我們的代碼顯得臃腫不堪。
所以在JDK8中引入了Optional,養成使用Optional的習慣後你可以寫出更優雅的代碼來避免空指針異常。
並且在很多函數式編程相關的API中也都用到了Optional,如果不會使用Optional也會對函數式編程的學習造成影響。
Optional就好像是包裝類,可以把我們的具體數據封裝Optional對象內部。然後我們去使用Optional中封裝好的方法操作封裝進去的數據就可以非常優雅的避免空指針異常。
我們一般使用 Optional 的 靜態方法ofNullable 來把數據封裝成一個Optional對象。無論傳入的參數是否為null都不會出現問題。
你可能會覺得還要加一行代碼來封裝數據比較麻煩。但是如果改造下getAuthor方法,讓其的返回值就是封裝好的Optional的話,我們在使用時就會方便很多。
而且在實際開發中我們的數據很多是從資料庫獲取的。Mybatis從3.5版本可以也已經支持Optional了。我們可以直接把方法的返回值類型定義成Optional類型,MyBastis會自己把數據封裝成Optional對象返回。封裝的過程也不需要我們自己操作。
如果你 確定一個對象不是空 的則可以使用 Optional 的 靜態方法of 來把數據封裝成Optional對象。
但是一定要注意,如果使用of的時候傳入的參數必須不為null。(嘗試下傳入null會出現什麼結果)
如果一個方法的返回值類型是Optional類型。而如果我們經判斷發現某次計算得到的返回值為null,這個時候就需要把null封裝成Optional對象返回。這時則可以使用 Optional 的 靜態方法empty 來進行封裝。
所以最後你覺得哪種方式會更方便呢? ofNullable
我們獲取到一個Optional對象後肯定需要對其中的數據進行使用。這時候我們可以使用其 ifPresent 方法對來消費其中的值。
這個方法會判斷其內封裝的數據是否為空,不為空時才會執行具體的消費代碼。這樣使用起來就更加安全了。
例如,以下寫法就優雅的避免了空指針異常。
如果我們想獲取值自己進行處理可以使用get方法獲取,但是不推薦。因為當Optional內部的數據為空的時候會出現異常。
如果我們期望安全的獲取值。我們不推薦使用get方法,而是使用Optional提供的以下方法。
我們可以使用filter方法對數據進行過濾。如果原本是有數據的,但是不符合判斷,也會變成一個無數據的Optional對象。
我們可以使用isPresent方法進行是否存在數據的判斷。如果為空返回值為false,如果不為空,返回值為true。但是這種方式並不能體現Optional的好處, 更推薦使用ifPresent方法 。
Optional還提供了map可以讓我們的對數據進行轉換,並且轉換得到的數據也還是被Optional包裝好的,保證了我們的使用安全。
例如我們想獲取作家的書籍集合。
只有一個抽象方法 的介面我們稱之為函數介面。
JDK的函數式介面都加上了 @FunctionalInterface 註解進行標識。但是無論是否加上該註解只要介面中只有一個抽象方法,都是函數式介面。
我們在使用lambda時,如果方法體中只有一個方法的調用的話(包括構造方法),我們可以用方法引用進一步簡化代碼。
我們在使用lambda時不需要考慮什麼時候用方法引用,用哪種方法引用,方法引用的格式是什麼。我們只需要在寫完lambda方法發現方法體只有一行代碼,並且是方法的調用時使用快捷鍵嘗試是否能夠轉換成方法引用即可。
當我們方法引用使用的多了慢慢的也可以直接寫出方法引用。
類名或者對象名::方法名
其實就是引用類的靜態方法
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了某個類的靜態方法 ,並且我們把要重寫的 抽象方法中所有的參數都按照順序傳入了這個靜態方法中 ,這個時候我們就可以引用類的靜態方法。
例如:
如下代碼就可以用方法引用進行簡化
注意,如果我們所重寫的方法是沒有參數的,調用的方法也是沒有參數的也相當於符合以上規則。
優化後如下:
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了某個對象的成員方法 ,並且我們把要重寫的 抽象方法中所有的參數都按照順序傳入了這個成員方法中 ,這個時候我們就可以引用對象的實例方法
例如:
優化後:
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了第一個參數的成員方法 ,並且我們把要 重寫的抽象方法中剩餘的所有的參數都按照順序傳入了這個成員方法中 ,這個時候我們就可以引用類的實例方法。
例如:
優化後如下:
如果方法體中的一行代碼是構造器的話就可以使用構造器引用。
如果我們在重寫方法的時候,方法體中 只有一行代碼 ,並且這行代碼是 調用了某個類的構造方法 ,並且我們把 要重寫的抽象方法中的所有的參數都按照順序傳入了這個構造方法中 ,這個時候我們就可以引用構造器。
例如:
優化後:
我們之前用到的很多Stream的方法由於都使用了泛型。所以涉及到的參數和返回值都是引用數據類型。
即使我們操作的是整數小數,但是實際用的都是他們的包裝類。JDK5中引入的自動裝箱和自動拆箱讓我們在使用對應的包裝類時就好像使用基本數據類型一樣方便。但是你一定要知道裝箱和拆箱肯定是要消耗時間的。雖然這個時間消耗很下。但是在大量的數據不斷的重復裝箱拆箱的時候,你就不能無視這個時間損耗了。
所以為了讓我們能夠對這部分的時間消耗進行優化。Stream還提供了很多專門針對基本數據類型的方法。
例如:mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。
當流中有大量元素時,我們可以使用並行流去提高操作的效率。其實並行流就是把任務分配給多個線程去完全。如果我們自己去用代碼實現的話其實會非常的復雜,並且要求你對並發編程有足夠的理解和認識。而如果我們使用Stream的話,我們只需要修改一個方法的調用就可以使用並行流來幫我們實現,從而提高效率。
parallel方法可以把串列流轉換成並行流。
也可以通過parallelStream直接獲取並行流對象。
⑵ 求科普什麼是函數式編程語言
實際上,函數式編程沒有一個嚴格的官方定義。嚴格上來講,函數式編程中的「函數」,並不是指我們編程語言中的「函數」概念,而是指數學「函數」或者「表達式」(例如:y=f(x))。不過,在編程實現的時候,對於數學「函數」或「表達式」,我們一般習慣性地將它們設計成函數。所以,如果不深究的話,函數式編程中的「函數」也可以理解為編程語言中的「函數」。
⑶ 幾種編程思想(鏈式編程,響應式編程,函數
函數響應式編程(Functional Reactive Programming:FRP)是一種和事件流有關的編程方式,其角度類似EventSoucing,關注導致狀態值改變的行為事件,一系列事件組成了事件流。FRP是更加有效率地處理事件流,而無需顯式去管理狀態。具體來說,FRP包括兩個核心觀點:1.事件流,離散事件序列2.屬性properties, 代表模型連續的值。一系列事件是導致屬性值發生變化的原因。FRP非常類似於GOF的觀察者模式。
⑷ 函數式編程的特點
函數式編程具有五個鮮明的特點。
1、函數是"第一等公民"
所謂"第一等公民"(first class),指的是函數與其他數據類型一樣,處於平等地位,可以賦值給其他變數,也可以作為參數,傳入另一個函數,或者作為別的函數的返回值。
2、只用"表達式",不用"語句"
"表達式"(expression)是一個單純的運算過程,總是有返回值;"語句"(statement)是執行某種操作,沒有返回值。函數式編程要求,只使用表達式,不使用語句。也就是說,每一步都是單純的運算,而且都有返回值。
3、沒有"副作用"
所謂"副作用"(side effect),指的是函數內部與外部互動(最典型的情況,就是修改全局變數的值),產生運算以外的其他結果。
4、不修改狀態
上一點已經提到,函數式編程只是返回新的值,不修改系統變數。因此,不修改變數,也是它的一個重要特點。
5、引用透明性
函數程序通常還加強引用透明性,即如果提供同樣的輸入,那麼函數總是返回同樣的結果。就是說,表達式的值不依賴於可以改變值的全局狀態。
⑸ java函數式編程是炫技編程嗎
函數式編程(Functional Programming)是一種編程風格,它是相對於指令式編程風格而言的,常見的面向對象編程就是指令式鬧哪蔽編程風格。
指令式編程是面向計算機硬體的抽象,有變數(對應著存儲單元),賦值語句(獲取、存儲指令),緩李表達式(內存引用和算術運算)和控制語句(跳轉語句)。
而函數式編程是面向數學的抽象,將計算描述為一種表達式求值。這里的函數實際就是數學中的函數,即自變數到因變數的映射。也就是說,一個函數的值僅決定於函數參數的值,不依賴其他狀態。
函數式編程是一種抽象程度很高的編程範式,純粹的函數式編程語言編寫的函數沒有變數,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之為沒有液州副作用。而允許使用變數的程序設計語言,由於函數內部的變數狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。
在函數式語言當中,函數作為一等公民,可以在任何地方定義,在函數內或函數外,可以作為函數的參數或返回值,可以對函數進行組合,也可以將函數賦值給變數。嚴格意義上的函數式編程意味著不適用可變的變數,賦值,循環和其他命令式控制結構進行編程。
函數式編程風格帶來的好處是:
函數式編程使用不可變對象作為變數,不會修改變數的值,而是返回一個新的值,如此這樣,更容易理清頭緒,使得單元測試和調試更加容易;
可以很自由地傳遞不可變對象,但對於可變對象來說,傳遞給其他代碼之前,需要先建造一個以防萬一的副本;
一旦不可變對象完成構造以後,就不會有線程因為並發訪問而破壞對象內部狀態,因為根本沒有線程可以改變不可變對象的狀態;
不可變對象讓哈希表鍵值更安全,所以哈希表鍵要求必須是不可變對象,否則使用可變對象,如果對象狀態發生變化,那麼在哈希表中就找不到這個對象了;
具體到編程語言,Scala(靜態語言)和Python(動態語言)都能比較的支持函數式編程風格,但是它們都不是純函數式的,也就是說它們同時支持指令式風格和函數式風格。而Java基本是指令式風格,但自從Java8引入lambda表達式以後也開始部分支持函數式風格。函數式編程最典型的是諸如map, flatMap, rece, filter等函數,它們的特點是支持某個函數作為上面這些函數的參數
⑹ 什麼是編程範式
編程範式Programming paradigm是指計算機中編程的典範模式或方法。
常見的編程範式有:函數式編程、程序編程、面向對象編程、指令式編程等。
不同的編程語言也會提倡不同的「編程范型」。一些語言是專門為某個特定的范型設計的,如Smalltalk和Java支持面向對象編程。而Haskell和Scheme則支持函數式編程。現代編程語言的發展趨勢是支持多種范型,如 C#、Java 8+、Kotlin、 Scala、ES6+ 等等。
(6)函數式編程和流編程擴展閱讀
編程範式中函數式編程的優點及應用情況:
1、易於重構、調試、測試。
2、整體應用:數學計算、人工智慧。
3、局部應用:已遍地開花。