摘要:表達(dá)式體現(xiàn)了函數(shù)式編程的思想,即一個(gè)函數(shù)亦可以作為另一個(gè)函數(shù)參數(shù)和返回值,使用了函數(shù)作參數(shù)返回值的函數(shù)被稱為高階函數(shù)。對流對象進(jìn)行及早求值,返回值不在是一個(gè)對象。
Java8主要的改變是為集合框架增加了流的概念,提高了集合的抽象層次。相比于舊有框架直接操作數(shù)據(jù)的內(nèi)部處理方式,流+高階函數(shù)的外部處理方式對數(shù)據(jù)封裝更好。同時(shí)流的概念使得對并發(fā)編程支持更強(qiáng)。
在語法上Java8提供了Lambda表達(dá)式來傳遞方法體,簡化了之前方法必須藏身在不必要的類中的繁瑣。Lambda表達(dá)式體現(xiàn)了函數(shù)式編程的思想,即一個(gè)函數(shù)亦可以作為另一個(gè)函數(shù)參數(shù)和返回值,使用了函數(shù)作參數(shù)/返回值的函數(shù)被稱為高階函數(shù)。
1. Lambda表達(dá)式Java 被詬病為繁瑣的地方就在于不支持傳遞方法,Java中的方法必須依賴類存在,也不能將方法作為參數(shù)或返回值,這是與python等語言相比的弱勢。Java 8中使用新特性Lambda表達(dá)式來改善這一點(diǎn)。
1.1 使用示例以Runnable接口為例,如果需要執(zhí)行一個(gè)線程,實(shí)際只需要run()方法中的代碼塊,但形式上必須要先制造一個(gè)Runnable接口實(shí)現(xiàn)類(通常是匿名內(nèi)部類)。
使用Lambda表達(dá)式僅僅需要一行代碼,達(dá)到傳遞run方法的效果,而不必定義匿名內(nèi)部類。
new Thread(()->System.out.println("Lambda")).start();1.2 類型參數(shù)推斷機(jī)制(Type Argument Inference)
Lambda表達(dá)式之所以能夠做如此簡化得益于Java的類型參數(shù)推斷機(jī)制。所有省略的內(nèi)容都可以由編譯器通過上下文推斷出來。
類型推斷機(jī)制在Java中的應(yīng)用廣泛,例如數(shù)組類型確定,Java7引入的菱形操作符等等。
類型參數(shù)推斷機(jī)制要推斷的是Lambda表達(dá)式的目標(biāo)類型,往往需要與Java的重載解析機(jī)制配合。其解析規(guī)則是
只有一個(gè)可能目標(biāo)類型時(shí),由響應(yīng)函數(shù)接口里的參數(shù)類型推導(dǎo)得出
有多個(gè)可能目標(biāo)類型,選擇最具體的類型
有多個(gè)可能目標(biāo)類型但無法明確最具體類型,則編譯報(bào)錯(cuò)
1.3 函數(shù)接口(Functional Interface)一個(gè)方法可以抽象成函數(shù)接口。函數(shù)接口類似于一個(gè)黑箱,只需要關(guān)注其參數(shù)和返回值類型,函數(shù)接口中只有單方法。Runnable的函數(shù)接口如下:
可以看到這是一個(gè)空接口。可以用它代表所有參數(shù)和返回值都為空的方法。
Java8中定義若干函數(shù)接口(位于包java.util.function)。
接口 | 參數(shù) | 返回類型 | 示例 |
---|---|---|---|
Pridicate |
T | boolean | 條件判斷 |
Consumer |
T | void | 消費(fèi)者 |
Function |
T | R | 再加工 |
Supplier |
void | T | 供應(yīng)者 |
BinaryOperator |
T | 求和 | |
UnaryOperator |
T | T | 邏輯非 |
以Pridicate函數(shù)接口為例,這是一個(gè)泛型接口,參數(shù)可以是任意類型,返回值是boolean類型,代表根據(jù)數(shù)值作判斷的一類方法。
1.4 并非語法糖從類型推斷的角度看很容易覺得Lambda表達(dá)式是和泛型,裝箱等機(jī)制一樣的語法糖,編譯器在背后補(bǔ)全了省略信息,但實(shí)際上并非如此。
class Apple{ public String toString() {return "apple";}; Runnable r1 = ()->{System.out.println(this);}; Runnable r2 = new Runnable() { public void run() { System.out.println(this); } }; } -------------------- //執(zhí)行兩個(gè)線程得到的結(jié)果是 apple Day0917.Apple$1@22e90474
正常的匿名內(nèi)部類中 this關(guān)鍵字 指向內(nèi)部類對象自身,同時(shí)將生成Apple$1.class文件。
Lambda表達(dá)式中this所指向的則是外部類對象,并不會生成內(nèi)部類class文件,這說明Lambda表達(dá)式并不是語法糖,它沒有產(chǎn)生一個(gè)內(nèi)部類,也沒有引入一個(gè)新的作用域。
1.5 默認(rèn)方法Lambda與內(nèi)部類相同之處在于其內(nèi)部所定義的變量均為final或既成事實(shí)上的final.
Java8最重要的改變就是對類庫的改造,使得接口中方法可以擁有代碼體。這種定義在接口中的包含方法體的方法,需要用default修飾,稱之為默認(rèn)方法。
interface Apple{ default void show(){ System.out.println("interface"); } } class MyApple implements Apple{ @Override public void show() { Apple.super.show(); } }
如果實(shí)現(xiàn)類中重寫了默認(rèn)方法,則接口中默認(rèn)方法就被覆蓋了。
如果兩個(gè)接口定義了相同的默認(rèn)方法,則實(shí)現(xiàn)類中可以通過指定全稱來確定使用哪個(gè)父類的方法。
如果將匿名內(nèi)部類改造為Lambda表達(dá)式是偷懶的話,那方法引用則是懶到連Lambda表達(dá)式都不想寫了。
在之前,我們知道Lambda表達(dá)式可以作為函數(shù)參數(shù)和返回值,表示傳遞一個(gè)方法。方法引用就是使用 ClassName::MethodName 的形式來指定方法。故而方法引用與Lambda表達(dá)式完全同源同種,可以相互替代。
//1,建立一個(gè)字符串 String::new //2.建立一個(gè)字符串?dāng)?shù)組 String[]::new
2. 流stream注意 lambda表達(dá)式與方法引用表示的是方法本身,將要被用過高階函數(shù)的參數(shù)/返回值,并不能多帶帶使用。
任務(wù):創(chuàng)建一個(gè)姓名集合,要求出所有初始字母為a的人的總數(shù)目。使用流處理的代碼如下:
ArrayListperson = new ArrayList<>(); ----init---- //1.由集合獲得流對象 Stream steam = person.stream(); //2.對流對象進(jìn)行過濾和統(tǒng)計(jì) steam.filter((s)->s.startsWith("a")) //1.流過濾 .count(); //2.計(jì)算流對象中元素?cái)?shù)目
使用函數(shù)接口(形式上表現(xiàn)為Lambda表達(dá)式)作為參數(shù)和返回值的函數(shù)就是所謂的高階函數(shù),如此處的filter,其參數(shù)為函數(shù)接口Predicate,亦可以理解為一個(gè)接口為 T--->boolean 的方法。
上述示例中為流對象的高階函數(shù)傳入一個(gè)函數(shù)接口Predicate,避免了直接處理集合中的數(shù)據(jù)對象。示例展示了流使用的通用格式:
獲得流對象Stream
對流對象Stream進(jìn)行惰性求值,返回值仍然是一個(gè)Stream對象。
對流對象Stream進(jìn)行及早求值,返回值不在是一個(gè)Stream對象。
2.1常見高階函數(shù) 1.collect方法collect方法屬于一個(gè)及早求值方法,負(fù)責(zé)將流對象轉(zhuǎn)換成其他數(shù)據(jù)結(jié)構(gòu),如列表,集合,值等。這項(xiàng)工作由收集器Collector完成。java8為此提供了Collectors工具類。
1.1 轉(zhuǎn)換成集合Listlist = stream.collect(Collectors.toList()); List arraylist = stream.collect(Collectors.toCollection(ArrayList::new)); Set set = stream.collect(Collectors.toSet()); Set treeSet = stream.collect(Collectors.toCollection(TreeSet::new));
使用Collectors.toList()將流對象轉(zhuǎn)換成集合時(shí)并不需要指定具體類型,Java默認(rèn)選擇了實(shí)現(xiàn)類型,如果要自己指定,可以使用Collectors.toCollection(ArrayList::new),其參數(shù)ArrayList::new就是上文中的方法引用,表示一個(gè)建立ArrayList對象的方法,ArrayList就是想要轉(zhuǎn)換成的數(shù)據(jù)類型;
1.2 轉(zhuǎn)換成值//1.獲得最大最小值 Function1.3 數(shù)據(jù)分塊getLevel = p->p.age; Comparator comparator = Comparator.comparing(getLevel); stream.collect(Collectors.maxBy(comparator)); stream.collect(Collectors.minBy(comparator)); //2.獲得平均值 ToIntFunction getAverage = p->p.age; stream.collect(Collectors.averagingInt(getAverage));
將流對象按某種條件分成兩部分
Predicate1.4 數(shù)據(jù)分組isTang = p->p.country.equals(Country.Tang); stream.collect(Collectors.partitioningBy(isTang));
Functioncountry= p -> p.country.ordinal(); stream.collect(Collectors.groupingBy(country));
1.5 字符串分塊和分組看似相同,但意義不同,分塊使用判斷作為方法,只能將流分成兩塊;分組則靈活的多。
stream.map(Person::getName).collect(Collectors.joining("/", "[", "]"));1.6 合并收集器
stream.collect(Collectors.groupingBy(country,Collectors.counting()));2.map
map是一個(gè)惰性求值方法。函數(shù)接口為Function
filter 是一個(gè)惰性求值方法。函數(shù)接口為Pridicate
flatMap 是一個(gè)惰性求值方法。其參數(shù)亦為Function
//1.a1,a2是兩個(gè)列表,map處理后仍是兩個(gè)列表 Stream.of(a1,a2).map(s->s) ------------- [1, 2, 3, 4] [] //2.flatMap將二者合并為一個(gè)流 Stream.of(a1,a2).map(s->s) .flatMap(s->s.stream()) ------------- 1234
看源碼可知,flatMap中函數(shù)接口Function的輸出類型為Stream
屬于一個(gè)及早求值方法。需要傳入一個(gè)Comparator函數(shù)接口,Java8提供了Comparator.comparing方法獲得該函數(shù)接口的實(shí)現(xiàn),該靜態(tài)方法是接口的靜態(tài)方法,獲得一個(gè)函數(shù)返回一個(gè)Comparator對象。
min(Comparator.comparing(s->s.toString()));
6.reducemax/min的返回值是 Optional,代表一個(gè)或有或無的值,主要是用來取代萬惡的null值;使用get方法可以獲取其值。
屬于一個(gè)及早求值方法。意為流數(shù)據(jù)的累加,有兩個(gè)版本。
//1.無初始值累加 T t = person.stream().reduce((a,b)->a+b); //2.帶初始值累加 Optional7. foreacht = person.stream().reduce("1",(a,b)->a+b);
屬于一個(gè)及早求值方法,用來遍歷流對象。
總而言之,Java8中流對象的引入使得可以在更高的層次上對集合進(jìn)行處理,使得抽象的方法和具體的行為邏輯分離開來,也加強(qiáng)了數(shù)據(jù)的封裝性,另一個(gè)好處是對并發(fā)的支持更強(qiáng),以后再補(bǔ)充。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/64737.html
摘要:內(nèi)部迭代與使用迭代器顯式迭代的集合不同,流的迭代操作是在背后進(jìn)行的。流只能遍歷一次請注意,和迭代器類似,流只能遍歷一次。 流(Stream) 流是什么 流是Java API的新成員,它允許你以聲明性方式處理數(shù)據(jù)集合(通過查詢語句來表達(dá),而不是臨時(shí)編寫一個(gè)實(shí)現(xiàn))。就現(xiàn)在來說,你可以把它們看成遍歷數(shù)據(jù)集的高級迭代器。此外,流還可以透明地并行處理,你無需寫任何多線程代碼了!我會在后面的筆記中...
摘要:一個(gè)抽象類可以通過實(shí)例變量字段保存一個(gè)通用狀態(tài),而接口是不能有實(shí)例變量的。分組和分區(qū)分組多級分組分區(qū)是分組的特殊情況由一個(gè)謂詞返回一個(gè)布爾值的函數(shù)作為分類函數(shù),它稱分區(qū)函數(shù)。 一、基本概念 兩個(gè)新特性: 1、函數(shù)式編程簡化代碼復(fù)雜度(引入了Lambda表達(dá)式) 2、更高效的利用多核CPU 1、基本概念: 1、Lambda基本語法 (parameters) -> expression 對...
摘要:語法中接口可以包含實(shí)現(xiàn)方法,需要使用修飾,此類方法稱為默認(rèn)方法。核心特性接口默認(rèn)方法就介紹到這里了,后續(xù)小樂會繼續(xù)講述核心特性。 JAVA8已經(jīng)發(fā)布很久,是自java5(2004年發(fā)布)之后Oracle發(fā)布的最重要的一個(gè)版本。其中包括語言、編譯器、庫、工具和JVM等諸多方面的新特性,對于國內(nèi)外互聯(lián)網(wǎng)公司來說,Java8是以后技術(shù)開發(fā)的趨勢。這里主要講解在開發(fā)中幾個(gè)核心的新特性。(主要從...
摘要:大家好,上一篇小樂給大家講述了樂字節(jié)核心特性表達(dá)式,點(diǎn)擊回顧。接下來繼續(xù)核心特性之函數(shù)式接口。感謝大家欣賞小樂帶來的核心特性之函數(shù)式接口,接下來還會更多核心技術(shù)講解,請關(guān)注樂字節(jié)如需要視頻課程,請搜索樂字節(jié)騰訊課堂 大家好,上一篇小樂給大家講述了《樂字節(jié)-Java8核心特性-Lambda表達(dá)式》,點(diǎn)擊回顧。接下來繼續(xù):Java8核心特性之函數(shù)式接口。 什么時(shí)候可以使用Lambda?通常...
摘要:的主要新特性表達(dá)式允許把函數(shù)作為一個(gè)方法的參數(shù)傳遞進(jìn)方法中。作用解決被詬病的匿名內(nèi)部類的問題。新特性模塊系統(tǒng)模塊是一個(gè)包的容器,最大的變化之一是引入模塊系統(tǒng)。支持標(biāo)準(zhǔn)標(biāo)準(zhǔn)是協(xié)議的最新版本,新的支持和流以及服務(wù)器推送特性。 Java 8 的主要新特性 1. Lambda 表達(dá)式 Lambda 允許把函數(shù)作為一個(gè)方法的參數(shù)傳遞進(jìn)方法中。 作用:解決 Java 被詬病的匿名內(nèi)部類的問題。 2...
閱讀 2976·2023-04-26 02:25
閱讀 2249·2023-04-25 18:05
閱讀 647·2021-09-30 09:57
閱讀 2942·2021-09-27 14:10
閱讀 1652·2019-08-30 15:44
閱讀 1003·2019-08-29 15:28
閱讀 2524·2019-08-29 14:10
閱讀 2263·2019-08-29 13:30