Java函數(shù)式編程--入門

引用來源

https://www.cnblogs.com/snowInPluto/p/5981400.html
https://my.oschina.net/joshuashaw/blog/487322
https://blog.csdn.net/yangjiachang1203/article/details/52619795
https://www.cnblogs.com/invoker-/p/7709052.html

Lambda表達式

java 8引入了Lambda表達式随闪,用于簡化代碼編寫。

  1. demo
button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent event) {
        System.out.println("button clicked");
    }
});

上面這段代碼骚勘,使用lambda表達式 可以簡化為:

button.addActionListener(event -> System.out.println("button clicked"));
  1. Lambda表達式的五種形式
    2.1 不包含參數(shù),使用空括號 () 表示沒有參數(shù)铐伴。例如撮奏,接口Runnable只有一個方法run。
Runnable noArguments = () -> System.out.println("Hello World");

該 Lambda 表達式 實現(xiàn)了 Runnable 接口,該接口也只有一個 run 方法,沒有參數(shù),且返回類型為 void当宴。

2.2 包含且只包含一個參數(shù),可省略參數(shù)的括號

ActionListener oneArgument = event -> System.out.println("button clicked");

2.3 Lambda 表達式的主體不僅可以是一個表達式,而且也可以是一段代碼塊,使用大括號 ({})將代碼塊括起來畜吊。該代碼塊和普通方法遵循的規(guī)則別無二致,可以用返 回或拋出異常來退出。只有一行代碼的 Lambda 表達式也可使用大括號,用以明確 Lambda表達式從何處開始户矢、到哪里結(jié)束玲献。

Runnable multiStatement = () -> {
    System.out.print("Hello");
    System.out.println(" World");
};

2.4 Lambda 表達式也可以表示包含多個參數(shù)的方法,如?所示。這時就有必要思考怎樣去閱 讀該 Lambda 表達式逗嫡。這行代碼并不是將兩個數(shù)字相加,而是創(chuàng)建了一個函數(shù),用來計算 兩個數(shù)字相加的結(jié)果青自。變量 add 的類型是 BinaryOperator。

BinaryOperator<Long> add = (x, y) -> x + y;

2.5 Lambda 表達式都可以擴寫為原始的“匿名類”形式驱证。當你覺得這個 Lambda 表達式很復雜不容易理解的時候延窜,不妨把它擴寫為“匿名類”形式來看。

BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;

函數(shù)接口

  1. 定義
    所謂的函數(shù)式接口抹锄,當然首先是一個接口逆瑞,然后就是在這個接口里面只能有一個抽象方法。這種類型的接口也稱為SAM接口伙单,即Single Abstract Method interfaces获高。
    • 函數(shù)式接口里是可以包含默認方法,因為默認方法不是抽象方法吻育,其有一個默認實現(xiàn)念秧,所以是符合函數(shù)式接口的定義的;
    • 函數(shù)式接口里是可以包含靜態(tài)方法布疼,因為靜態(tài)方法不能是抽象方法摊趾,是一個已經(jīng)實現(xiàn)了的方法,所以是符合函數(shù)式接口的定義的游两;
    • 函數(shù)式接口里是可以包含Object里的public方法砾层,這些方法對于函數(shù)式接口來說,不被當成是抽象方法(雖然它們是抽象方法)贱案;因為任何一個函數(shù)式接口的實現(xiàn)肛炮,默認都繼承了Object類,包含了來自java.lang.Object里對這些抽象方法的實現(xiàn)宝踪;
    • 函數(shù)式接口里允許子接口繼承多個父接口侨糟,但每個父接口中都只能存在一個抽象方法,且必須的相同的抽象方法瘩燥。
  2. 常用的核心函數(shù)接口
    • Predicate<T>粟害,參數(shù):T,返回boolean颤芬。用于判別一個對象悲幅。比如求一個人是否為男性
    • Consumer<T> 套鹅,參數(shù)T,返回void汰具。用于接收一個對象進行處理但沒有返回卓鹿,比如接收一個人并打印他的名字
    • Function<T, R> ,參數(shù)T留荔,返回R吟孙。用于轉(zhuǎn)換一個對象為不同類型的對象。
    • Supplier<T> 聚蝶,無參數(shù)杰妓。返回T。用于提供一個對象碘勉。
    • UnaryOperator<T>巷挥,參數(shù)T,返回T验靡。用于接收對象并返回同類型的對象倍宾。
    • BinaryOperator<T>,參數(shù)(T, T)胜嗓,返回T高职。用于接收兩個同類型的對象,并返回一個原類型對象辞州。
    • 其中 Cosumer 與 Supplier 對應(yīng)屏镊,一個是消費者炕置,一個是提供者劝枣。Predicate 用于判斷對象是否符合某個條件明棍,經(jīng)常被用來過濾對象。Function 是將一個對象轉(zhuǎn)換為另一個對象牵啦,比如說要裝箱或者拆箱某個對象。UnaryOperator 接收和返回同類型對象妄痪,一般用于對對象修改屬性哈雏。BinaryOperator 則可以理解為合并對象。

Stream操作

  1. Java 8 中衫生,引入了流(Stream)的概念裳瘪,這個流和以前我們使用的 IO 中的流并不太相同。所有繼承自 Collection 的接口都可以轉(zhuǎn)換為 Stream罪针。demo:
class Person {
    public String name;
    public int age;
}

List<Person> personList;
統(tǒng)計personList中年齡大于20的數(shù)目:
long count = personList.stream()
             .filter(person -> person.getAge() > 20)
             .count();
  1. Stream 常用操作彭羹。Stream 的方法分為兩類。一類叫惰性求值泪酱,一類叫及早求值派殷。判斷一個操作是惰性求值還是及早求值很簡單:只需看它的返回值还最。如果返回值是 Stream,那么是惰性求值毡惜。其實可以這么理解拓轻,如果調(diào)用惰性求值方法,Stream 只是記錄下了這個惰性求值方法的過程经伙,并沒有去計算扶叉,等到調(diào)用及早求值方法后,就連同前面的一系列惰性求值方法順序進行計算帕膜,返回結(jié)果枣氧。通用形式為:
    Stream.惰性求值.惰性求值. ... .惰性求值.及早求值
  2. collect(toList())。由 Stream 里的值生成一個列表垮刹,是一個及早求值操作达吞。
List<String> collected = Stream.of("a", "b", "c")
                         .collect(Collectors.toList());

  1. map. 如果有一個函數(shù)可以將一種類型的值轉(zhuǎn)換成另外一種類型,map 操作就可以使用該函數(shù)危纫,將一個流中的值轉(zhuǎn)換成一個新的流宗挥。map 方法就是接受的一個 Function 的匿名函數(shù)類,進行的轉(zhuǎn)換种蝶。
List<String> collected = Stream.of("a", "b", "hello")
                               .map(string -> string.toUpperCase())
                               .collect(toList());
  1. filter契耿。遍歷數(shù)據(jù)并檢查其中的元素時,可嘗試使用 Stream 中提供的新方法 filter螃征。filter 方法就是接受的一個 Predicate 的匿名函數(shù)類搪桂,判斷對象是否符合條件,符合條件的才保留下來盯滚。
List<String> beginningWithNumbers = 
        Stream.of("a", "1abc", "abc1")
              .filter(value -> isDigit(value.charAt(0)))
              .collect(toList());
  1. flatMap. 可用 Stream 替換值踢械,然后將多個 Stream 連接成一個 Stream。flatMap 最常用的操作就是合并多個 Collection魄藕。
List<Integer> together = Stream.of(asList(1, 2), asList(3, 4))
                               .flatMap(numbers -> numbers.stream())
                               .collect(toList());
  1. max和min. Stream 上常用的操作之一是求最大值和最小值内列。Stream API 中的 max 和 min 操作足以解決這一問題。max 和 min 方法返回的是一個 Optional 對象.
List<Integer> list = Lists.newArrayList(3, 5, 2, 9, 1);
int maxInt = list.stream()
                 .max(Integer::compareTo)
                 .get();
int minInt = list.stream()
                 .min(Integer::compareTo)
                 .get();

Integer::compareTo 也是屬于 Java 8 引入的新特性背率,叫做 方法引用(Method References)话瞧。在這邊,其實就是 (int1, int2) -> int1.compareTo(int2) 的簡寫

  1. reduce. reduce 操作可以實現(xiàn)從一組值中生成一個值寝姿。上述例子中用到的 count交排、min 和 max 方法,因為常用而被納入標準庫中。事實上饵筑,這些方法都是 reduce 操作埃篓。reduce 的第一個參數(shù),是一個初始值根资。
int result = Stream.of(1, 2, 3, 4)
             .reduce(0, (acc, element) -> acc + element);
             
int result = Stream.of(1, 2, 3, 4)
             .reduce(1, (acc, element) -> acc * element);
  1. 數(shù)據(jù)并行化操作架专。并行化操作流只需改變一個方法調(diào)用同窘。如果已經(jīng)有一個 Stream 對象,調(diào)用它的 parallel() 方法就能讓其擁有并行操作的能力胶征。如果想從一個集合類創(chuàng)建一個流塞椎,調(diào)用 parallelStream() 就能立即獲得一個擁有并行能力的流。
int sumSize = Stream.of("Apple", "Banana", "Orange", "Pear")
              .parallel()
              .map(s -> s.length())
              .reduce(Integer::sum)
              .get();

如果你去計算這段代碼所花的時間睛低,很可能比不加上 parallel() 方法花的時間更長案狠。這是因為數(shù)據(jù)并行化會先對數(shù)據(jù)進行分塊,然后對每塊數(shù)據(jù)開辟線程進行運算钱雷,這些地方會花費額外的時間骂铁。并行化操作只有在 數(shù)據(jù)規(guī)模比較大 或者 數(shù)據(jù)的處理時間比較長 的時候才能體現(xiàn)出優(yōu)勢,所以并不是每個地方都需要讓數(shù)據(jù)并行化罩抗,應(yīng)該具體問題具體分析拉庵。

  1. 收集器collect。Stream 轉(zhuǎn)換為 List 是很常用的操作套蒂,其他 Collectors 還有很多方法钞支,可以將 Stream 轉(zhuǎn)換為 Set, 或者將數(shù)據(jù)分組并轉(zhuǎn)換為 Map,并對數(shù)據(jù)進行處理操刀。也可以指定轉(zhuǎn)換為具體類型烁挟,如 ArrayList, LinkedList 或者 HashMap。甚至可以自定義 Collectors骨坑,編寫自己的收集器撼嗓。
  2. 元素順序。一些集合類型中的元素是按順序排列的欢唾,比如 List且警;而另一些則是無序的,比如 HashSet礁遣。增加了流操作后斑芜,順序問題變得更加復雜。如果集合本身就是無序的祟霍,由此生成的流也是無序的杏头。一些中間操作會產(chǎn)生順序,比如對值做映射時浅碾,映射后的值是有序的,這種順序就會保留 下來续语。如果進來的流是無序的垂谢,出去的流也是無序的。如果我們需要對流中的數(shù)據(jù)進行排序疮茄,可以調(diào)用 sorted 方法滥朱。
List<Integer> list = Lists.newArrayList(3, 5, 1, 10, 8);
List<Integer> sortedList = list.stream()
                           .sorted(Integer::compareTo)
                           .collect(Collectors.toList());
  1. @FunctionalInterface. 每個用作函數(shù)接口的接口都應(yīng)該添加這個注釋根暑。但 Java 中有一些接口,雖然只含一個方法徙邻,但并不是為了使用 Lambda 表達式來實現(xiàn)的排嫌。比如,有些對象內(nèi)部可能保存著某種狀態(tài)缰犁,使用帶有一個方法的接口可能純屬巧合淳地。該注釋會強制 javac 檢查一個接口是否符合函數(shù)接口的標準。如果該注釋添加給一個枚舉類型帅容、類或另一個注釋颇象,或者接口包含不止一個抽象方法,javac 就會報錯并徘。重構(gòu)代碼時遣钳,使用它能很容易發(fā)現(xiàn)問題。

實際使用

  1. 場景描述:現(xiàn)在有很多用戶麦乞,每個用戶使用userId來標識蕴茴,每個用戶都有一定數(shù)量的money。Person類的定義如下:
@Data
class Person {
        private String userId;
        private Integer money;
}

現(xiàn)在有一個List<Person> personList姐直,如果需要計算所有person的money總和倦淀,使用Lambda表達式,可以是:

Integer sum = personList.stream().mapToInt(p->p.getMoney()).sum();
  1. 場景描述:現(xiàn)在有一個List<String> stringList简肴,要求將所有的String晃听,按照長度分類。使用Lambda表達式砰识,可以是:
Map<Integer,List<String>> result = stringList.stream().collect(Collectors.groupingBy(s -> s.length()));
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末能扒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子辫狼,更是在濱河造成了極大的恐慌初斑,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膨处,死亡現(xiàn)場離奇詭異见秤,居然都是意外死亡,警方通過查閱死者的電腦和手機真椿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門鹃答,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人突硝,你說我怎么就攤上這事测摔。” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵锋八,是天一觀的道長浙于。 經(jīng)常有香客問我,道長挟纱,這世上最難降的妖魔是什么羞酗? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮紊服,結(jié)果婚禮上檀轨,老公的妹妹穿的比我還像新娘。我一直安慰自己围苫,他們只是感情好裤园,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著剂府,像睡著了一般拧揽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腺占,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天淤袜,我揣著相機與錄音,去河邊找鬼衰伯。 笑死铡羡,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的意鲸。 我是一名探鬼主播烦周,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼怎顾!你這毒婦竟也來了读慎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤槐雾,失蹤者是張志新(化名)和其女友劉穎夭委,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體募强,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡株灸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了擎值。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慌烧。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鸠儿,靈堂內(nèi)的尸體忽然破棺而出屹蚊,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布淑翼,位于F島的核電站,受9級特大地震影響品追,放射性物質(zhì)發(fā)生泄漏玄括。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一肉瓦、第九天 我趴在偏房一處隱蔽的房頂上張望遭京。 院中可真熱鬧,春花似錦泞莉、人聲如沸哪雕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斯嚎。三九已至,卻和暖如春挨厚,著一層夾襖步出監(jiān)牢的瞬間堡僻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工疫剃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钉疫,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓巢价,卻偏偏與公主長得像牲阁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子壤躲,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Jav8中城菊,在核心類庫中引入了新的概念,流(Stream)柒爵。流使得程序媛們得以站在更高的抽象層次上對集合進行操作役电。...
    仁昌居士閱讀 3,638評論 0 6
  • Java8 in action 沒有共享的可變數(shù)據(jù),將方法和函數(shù)即代碼傳遞給其他方法的能力就是我們平常所說的函數(shù)式...
    鐵牛很鐵閱讀 1,233評論 1 2
  • 我喜歡一本古典文言短篇文言小說棉胀,它叫做《聊齋志異》法瑟!它為何會令我情有獨鐘呢?且聽我慢慢給大家到來唁奢! 《聊齋志異》...
    溟夜星辰閱讀 981評論 0 2