java中函数是什么_[一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念...
本文是針對于java8引入函數式編程概念以及stream流相關的一些簡單介紹
什么是函數式編程?
java程序員第一反應可能會理解成類的成員方法一類的東西
此處并不是這個含義,更接近是數學上的函數
看一下百度百科中關于函數的說明
函數的定義:
給定一個數集A,假設其中的元素為x。
現對A中的元素x施加對應法則f,記作f(x),得到另一數集B。假設B中的元素為y。
則y與x之間的等量關系可以用y=f(x)表示。
我們把這個關系式就叫函數關系式,簡稱函數。
函數概念含有三個要素:定義域A、值域C和對應法則f。
其中核心是對應法則f,它是函數關系的本質特征。
對應于編程來說,當然不是完全的數學上的函數定義
所謂函數式編程我們可以理解為:
通過對應法則f(x) 對指定的x 進行處理,映射成另外一個值
而且不會對x本身產生變動
所謂不會對x產生變動,你可以理解為無副作用,或者說副作用不會被察覺
副作用你可以理解為解題過程中對數據的修改
說起來好像很啰嗦,但是如果有人告訴你 通過sin(x) 計算后, x的值被改變了,你不會覺得異常奇怪么
函數式編程就是把函數的一些特性應用于編程語言之中
注意:
函數式編程不是某一種語言,也不是某個API
他是一種方法論,是一種編程范式,有它自有的一些特性和規定
語言中引入函數式編程,也就是用語言本身定義了函數式編程的一些特性和規定
函數式編程最重要的基礎是λ演算,而且λ演算的函數可以接受函數當作輸入(參數)和輸出(返回值)。
它一套用于研究函數定義、函數應用和遞歸的形式系統
我們只需要知道λ演算是一種形式的匿名函數,并且接收一個參數作為輸入 (可以柯里化進行參數轉換多參數函數轉換為單參數)
有興趣的可以去探究下λ演算
函數式編程有下列特性
閉包和高階函數
閉包就是能夠讀取其他函數內部變量的函數,是個不太好理解的概念
此處我們僅僅理解成 函數可以當做值進行傳遞并且可以使用變量保存 是"第一等公民"
一等公民或者一等類型的含義就是指可以跟值一樣的地位,作為參數傳遞或者存儲于變量中
高階函數是指可以用另一個函數(間接地,用一個表達式) 作為其輸入參數,比如 f(g(x))=g(x)+1 的形式
惰性計算
表達式不是在綁定到變量時立即計算,而是在求值程序需要產生表達式的值時進行計算
你可以理解為流水線上每一個節點都只是做了一系列的設置,并沒有立刻去計算數值
沒有副作用
副作用是指在運算過程中,修改了函數內部局部變量以外的其他變量的狀態,比如你修改了類成員變量
沒有副作用也就意味著不產生運算以外的其他結果,不修改系統的變量
引用透明性
如果提供同樣的輸入,那么函數總是返回同樣的結果
也就是說表達式的值不依賴于可以改變值的全局狀態,比如不依賴成員變量的值
為什么要使用函數式編程?
關注做什么 更接近于自然語言
任務分解后你的解題思路將是如何調用各個不同的函數,要做什么
不在關注于函數內部的細節本身去思考怎么做
假設有這么一組Student學生類型的List數據,學生有性別男女
如果在Java代碼中,你會如何解題?
偽代碼:
List男List ;for(int i=0;i
if(studentList[i].性別 ==男){
男List.add(List[i])
}
你循環遍歷列表,找到符合條件的學生,然后把他加入另外一個列表,這可能是一種常見的解題思路
假設有個Student 學生表,每條記錄都有一個性別字段值為男女
如果是在數據庫中查詢呢,一種可能的解法是這樣子的
select
*
fromstudentwheresex='男';
他們的主要區別是什么?
一個最直觀的差別就是:
java代碼中是你自己去循環數據項,你自己處理每一項數據,找出符合你要求的數據
SQL查詢中,你只是傳入通知條件where? sex='男';? ,數據庫在自己內部進行了循環,幫我們找出來符合要求的數據
這就是外部循環和內部循環,這是一種思維方式的轉變
外部循環,需要程序員自己去關注每一個數據項
內部循環,程序員只需要關注結果
內部循環以及函數調用 也將我們從如何做中解放出來,讓我們不再關注數據項循環的細節本身,僅僅關注于此次調用的結果
不管是什么方式進行思考編程,你都會將你的任務進行分解
劃分為更小的子任務
但是不同的是:
在如何做的思維下,你還需要思考在每個子任務中,每一個細節是怎么處理的,比如循環中進行條件判斷
這其實還是往計算機的思維傾斜的一種思考方式,這是指令式或者命令式的編程模式
在做什么的思維下,你不在關注每個子任務的內部細節,只在乎結果也就是"做什么"
每個子任務內部的細節是函數自己內部的事情,這更加符合人的思維習慣
內部循環不也是函數式編程的一種表現形式么
函數本身如同一個黑盒一般,有輸入有輸出,我們不關心內部的實現細節,僅僅在乎輸入和輸出
內部循環也是如此,我們告訴他我們想要的結果行為,他返回給我們結果
比如SQL中
where?? sex='男';? 這就是對我們行為的描述(不要把它理解成篩選條件)
我們將行為像參數一樣傳遞給了數據庫軟件,數據庫執行查詢操作,根據的是我們給定的行為
這就是行為參數化的魅力所在
行為參數化也是一種思維模式,只要能把行為像參數一樣進行傳遞? 就是行為參數化
有人可能已經想到了匿名內部類
new Thread(newRunnable() {
@Overridepublic voidrun() {
System.out.println();
}
}).start();
的確這也是一種行為參數化,但是顯然,這段代碼還不夠簡潔純粹,因為方法的外層還套了一層對象
Java8中的行為參數化,傳遞的將是更加純粹的行為,而不再需要借助一個匿名對象的形式,而且,Lambda表達式不會像內部類一樣生成一個類
傳遞的是方法本身,方法中的代碼本身
那么行為參數化,不也就是函數式編程中的閉包特性么
更加易于并發編程
函數式編程的準則是沒有副作用不依賴外部的數據,也不改變外部數據的值。
我們知道線程安全的根本在于共享數據,如果沒有任何的數據共享,那么很多的并發/線程安全問題都將迎刃而解
所以說這一特性正好滿足了多核并行程序設計的需求,所以很顯然能夠簡化并行程序的開發
函數式編程代碼簡潔
函數式編程大量使用函數,減少了代碼的重復,就如同你調用別人的方法一樣不是么,一行就得到了結果
Java8 對于函數式編程的支持
編程語言把函數式編程的概念引入,也就是使自身支持函數式編程的特性,換句話說也就是
在語言內部可以使用一系列的類型或者關鍵字或者符號組合等進行表示
Java主要涉及這三個核心概念
函數接口(FunctionalInterface)
流(Stream)
收集器(Collector)
函數接口
既然函數式編程要求函數可以是同值一樣的一等公民用于參數化傳遞,那么必須要有表示函數的類型
先說一下函數式接口的注解
注解@FunctionalInterface描述了什么是一個函數式接口
public @interface FunctionalInterface {}
上面的注釋也就相當于是函數式接口的定義:
一個函數式接口只能有一個抽象方法,default方法有實現,所以不是抽象方法
如果一個接口聲明了一個覆蓋Object? public公有方法的抽象方法,也不算是抽象方法
所以說:函數式接口,有且僅有一個抽象方法,覆蓋Object的public方法不計算在內(如果是覆蓋Object的protected那么會計數的)
比如
java.lang.Runnable、java.util.Comparator是典型的函數式接口
函數接口是一個接口,有且只有一個唯一的抽象方法
接口上定義了函數的類型參數
抽象方法的方法簽名限定了函數(函數式接口的抽象方法的簽名稱為函數描述符)
所以說一個函數接口,只能描述一種類型的函數
比如
Function????? 這個函數接口
他表示形如
R function(T){
....
return R
}
他的類型參數是T? R,調用方法apply 輸入為T?? 輸出為R
作用為轉換一個對象為不同類型的對象
所有這種形式的函數都是這個函數接口類型
比如
public static voidmain(String[] args){
Function function = (String x)->x.equals("true");
System.out.println(function.apply("1"));
System.out.println(function.apply("true"));
}
至此,Java中已經有了用于表示函數的類型了,也就是可以定義一個函數或者返回一個函數,或者把函數當做一個參數值進行傳遞了
以賦值運算符的形式來類比的話就是
比如
int i = 1;
等號左邊的類型已經有了就是函數接口
但是右邊,也就是行為參數化這個行為到底如何表示呢?也即是上面的1 的位置
在java中可以使用
Lambda表達式((String x)->x.equals("true"))
方法引用(String::length)
兩種形式進行表示
比如
public static voidmain(String[] args){
Function function =String::length;
System.out.println(function.apply("1"));
System.out.println(function.apply("true"));
}
既然每一種函數類型都需要存在指定形式的函數接口,想要使用Lambda-匿名函數或者方法引用,自然需要定義函數接口
函數類型的說法可能不太準確,函數式接口的抽象方法的簽名稱為函數描述符 其實說的也都還是方法簽名? 方法簽名唯一的標識了一個函數
Java8 也已經給我們預置了一些常用的函數接口類型
已經定義一套能夠描述常見函數描述符的函數接口
比如上面提到的
function? 就是其中一種
另外還有其他一些,后面再說,我們已經可以在Java中表示一個函數,并且對函數進行調用
流
流,流動,流水,java中早就已經有了IO流,形象的表達了數據在程序中的處理與流動
Java8中的Stream流則更傾向于流水線的含義
每個節點有各自獨立的功能目的,根據你的目的(做什么),將各個獨立的功能目的節點拼接成一整個的完整的流水線
數據在此流水線上進行加工處理,最終得出結果
通過告知Stream "做什么" 來進行數據操作和處理
你不在需要關注內部的細節,Stream通過內部迭代進行數據項的篩選查找,找到符合條件的數據
流(Stream)是Java8對函數式編程的重要支撐。大部分函數式工具都圍繞Stream展開
也可以說Stream類是Java8 關于函數式編程定義的一些列函數集合
由此可以看得出來,Stream的重要性
想要使用Java進行函數式編程,僅僅使用Lambda表達式是不夠的,必須有足夠的函數,Lambda表達式只有跟stream一起使用才能顯示其真實的威力
集合是一種數據結構用于存儲數據
Stream不是一種數據結構,是對于數據的一種新的視圖,用于數據的計算,提供了一系列的API用于調用
概括的說
Stream就是函數式編程中編程語言提供出來的庫方法集合,而參數基本上都是函數
所以才說,Lambda表達式只有跟stream一起使用才能顯示其真實的威力
常用的Stream調用流程
1.獲得Stream
想要使用Stream的一些特性,顯然你必須把你的數據集轉換生成為Stream,這沒有Stream何談使用?
2.設置行為類型 也就是操作類型
這句話有些模糊不清,其實就是你需要設置想干什么
到底是篩選數據?轉換數據?求和還是怎樣?
你可以類比為SQL查詢中到底是SELECT 還是UPDATE 或者DELETE? 這就是行為的類型
為了更快理解的話,你可以片面的理解為調用Stream類的方法
我們舉例說明
比如你經常讓同學幫你買東西
買東西就是行為類型,是去買東西,既不是幫你開車也不是陪你看電影
這就是行為的類型
Stream中有一系列的API可以幫助我們達到這個目的
比如 filter? map等等
3. 確定行為參數 也就是操作內容
行為參數也就是基于已經設置的行為類型下,你具體要以什么樣子的行為去執行
你篩選數據篩選什么樣子的數據?
轉換數據,轉換為什么形式?
類比為SQL查詢中就是查詢條件,查詢? 男生?查詢 女生? 這就是行為的具體方式
還是剛才的例子,你經常讓同學幫你買東西,那到底買什么?買礦泉水還是買面包?這就是確定行為參數
Java8中使用方法引用或者Lambda-匿名函數? 或者方法引用來表示行為參數
4.行為的屬性
既然是流水線式的工作方式,那么當前的工作結束后或許結束了或許是進入到流水線的下一環節
當然最終他肯定還是會結束掉的
這就又涉及到綁定行為方法的屬性種類? 到底是中間的操作(可以繼續傳遞給流水線下一步)? 還是結束的終端操作
中間操作的返回結果還是一個Stream? 你仍舊可以對他進行上述類似的過程
終端操作則一般會將流進行收集整理成指定的數據結構
這基本上是一個常用的Stream使用流程
流程處理雖然很簡單,但是強大之處在于中間操作處理后仍舊是流
這就意味著你可以按照需要進行無數的變換組合以達到你想要的效果
收集器
Stream結合Lambda表達式可以對于數據進行各種各樣的操作
但是Stream 終歸是Stream ,它并不是一種數據結構,不管經過了多少處理,他終歸是再次返回到代碼中具體的其他數據類型中
把Stream類比做數據項處理的流水線的話
中間操作就是流水線上的一個個的功能操作節點
而收集器就是在某些結束操作中用于將數據進行轉換的工具
在Java中關于收集器有幾個關鍵的概念
1. Stream中的collect 方法是收集器的調用者
R collect(Collector super T, A, R> collector);
2. Collector 接口 定義了收集器
public interface Collector {
3. 收集器工廠Collectors? 用于預置一些收集器
public final class Collectors
比如?? .....collect(Collectors.toList());? 就是把一個處理后的流轉換為List
總結:
Java8 構建了三個主要概念,函數接口,流,收集器
有了函數接口? 函數擁有了類型也就是可以像值一樣作為參數進行傳遞,作為返回值,或者使用變量進行表示
使用Lambda-匿名函數或者方法引用來表示行為參數? 也就是函數的值
Stream是Java8 提供的函數式編程的"庫函數" 預定了一些常用的操作模式,通過Lambda表達式結合使用
收集器用于把Stream處理后的數據進行打包整理成你需要的數據結構
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的java中函数是什么_[一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 谷粒学院7-13天
- 下一篇: java 先进先出的map_「 深入浅出