函數式編程 讀書筆記

函數式編程css

函數式編程思想:在思考問題時,使用不可變值和函數,函數對一個值進行處理,映射成另外一個值。java

已經掌握的使用場景:git

一、      獲取集合中的最大或最小值,當集合類型爲自定義類型時的使用比較器程序員

二、      循環進行一些操做.foreEach( )編程

三、      統計符合條件的有多少個安全

List.stream().filter( 條件).count();app

 

    

 

 

.map(  ) : 方法將一個流中的值轉換成一個新的流dom

 

.filter(   ) :  方法將流進行過濾,保留符合條件的(返回爲true 的結果 )函數式編程

 

.strem(  )  方法 建立stream對象  我的理解爲將對象流化函數

 

Stream.of(  )  將參數中的一組初始值變爲一個新的流

 

.count(   )    方法計算給定 Stream 裏包含多少個對象

 

.collect( Collectors.toList(  )  )    從 Stream 中生成一個列表 能夠是toList( ), toSet( ), toMap(   )

 

 

flatMap   方法可用 Stream 替換值,而後將多個 Stream 鏈接成一個 Stream

 

List<Integer> together = Stream.of(asList(1, 2), asList(3, 4))

.flatMap(numbers -> numbers.stream())

.collect(toList());   

 

將集合 list1={1,2 } 的stream 和集合 list2={3,4 }的stream  轉換爲新的 stream

再經過 .collect(toList()); 將新的stream 轉爲一個list 集合

 

.max(  )       .min( )

   獲取一組Stream  中的最大值

    獲取集合中最大的值

List <   Integer> mylist =Arrays.asList(10,8,7,20,5);

    Integer num=mylist.stream().max(Integer::compareTo).get();

     

     獲取學生集合中年齡最大的學生

     List <Student> mylist = Arrays.asList(new Student(‘6,」aa」),new Stduent(20,」bb」));

     Student older = mylist.stream().max(Comparator.comping(Student::getAge)).get();

    

 

 

練習代碼:

package com.umuw.pigrecord.dao;

/**
 * Created by Jim Calark on 2017/3/30.
 */
public class temp {

    package test;
/**
 * @
描述: 練習函數式編程
 * @author Jim Calark
 *
 */

import java.util.ArrayList;

import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.omg.Messaging.SyncScopeHelper;
import org.w3c.dom.css.Counter;


    public class test {
        public static void main(String[] args) {

/**
 *
總結: 一、集合的求和、求乘積等對集合元素累積逐一操做
 * 二、求最大和最小
 * 三、合併集合
 * 四、獲取平均值
 * 五、數據分塊 和數據分組
 * 六、計數
 */





//1、 max 和    min     .get()方法   

            List< Integer> nums = Arrays.asList(9,8,7,6,5);
            //獲取集合中的最大值
            int max = nums.stream().max(Integer::compareTo).get();
            List <Student> students = Arrays.asList(new Student(6,"aa"),new Student(20,"bb"));

            //獲取年齡最小的學生
            //stream 返回一個optional 對象
            // get 方法能夠取出 Optional 對象中的值
            Student younger= students.stream().min(Comparator.comparing(Student::getAge)).get();

            /**
             * @
描述: 對比
             * nums.stream().max(Integer::compareTo).get();
             * students.stream().min(Comparator.comparing(Student::getAge)).get();
             *  結論: 基本數據類型  實現了 comparator 的比較方法
             */

// 2、  flatMap    .collect() 方法將流轉爲想要的集合


            //將多個集合合併爲一個集合
            //flatMap: 將多個流合併爲一個流
            //collect: 將流轉爲指定形式的集合
            List <Integer> alltoone = Stream.of(Arrays.asList(1,2),Arrays.asList(3,4))
                    .flatMap(a -> a.stream()).collect(Collectors.toList());


//三reduce    會返回一個最終值
            //求和  
            // reduce(param1,param2)
            //param1 是和集合中第一數計算的自定義數
            //param2 計算數的方法   括號中 第一個爲每次計算的當前值 第二個爲下一個數
            int sumnumber = alltoone.stream().reduce(0,(currentsum,nextnum)->currentsum+nextnum);
            //求乘積
            int  mulnumber =alltoone.stream().reduce(1,(currentmul,nextnum)->currentmul*nextnum);



//4、 filter
            //過濾    將返回爲true 的部分留下

            //獲取學生集合中有多少個成年人
            long adualtnum = students.stream().filter(s-> s.isAdualt(s)).count();

            //將學生集合中的成年人 取出爲一個新的集合
            List <Student> allAdualts = students.stream().filter(s-> s.isAdualt(s)).collect(Collectors.toList());




// 5、map   若是有一個函數能夠將一種類型的值轉換成另一種類型, map 操做就能夠
//          使用該函數,將一個流中的值轉換成一個新的流

            //將字符串轉換爲大寫的形式
            // 先用map 將流的形式轉換
            //再用collect 將轉換後的流生成一個新的集合
            List<String> strs = Stream.of("a","b","c","d").map(String::toUpperCase).collect(Collectors.toList());
            List<String> newStrs =Arrays.asList("a","b","c","d").stream().map(String::toUpperCase).collect(Collectors.toList());

//練習  獲取班級集合中全部年級大於 18 的學生的名字
            List<Student> stus = Arrays.asList(new Student(23,"小李"),new Student(13,"小楊"),new Student(29,"小錢"));
            List<Grades> grades= Arrays.asList(new Grades("一班",students),new Grades("二班",stus));

            List<String> stunames =  grades.stream().flatMap(g -> g.getStudents().stream())
                    .filter(a -> a.getAge() >18)
                    .map(a -> a.getName())
                    .collect(Collectors.toList());

// 獲取一個班級中的人數

            //注意要將班中的學生集合取出來再轉爲Stream 對象  count 返回的是一個long 型數據
            Grades gra = new Grades("一班",students);
            long num = gra.getStudents().stream().count();

// 練習  獲取一個string 中的小寫字母 個數
            String str = new String("abcASabc");
            Long lowercasenum =  str.chars().filter(Character::isLowerCase).count();


//練習 在一個字符串列表中,找出包含最多小寫字母的字符串     
            String str2= Stream.of(str,"ccccccc","AAAA")
                    .max(Comparator.comparing(a->(a.chars().filter(Character::isLowerCase)).count()) )
                    .get();

//平均每一個班級有多少學生     
            double aver=grades.stream().collect(Collectors.averagingInt(g -> g.getStudents().size() ));


// 數據分塊  partitioningBy
            //將學生分爲成年和未成年 兩部分,保存在一個map集合中
            //{false=[Student [age=13, name=小楊]], true=[Student [age=23, name=小李], Student [age=29, name=小錢]]}
            Map<Boolean ,List<Student>> mumaps=  stus.stream().collect(Collectors.partitioningBy(a -> a.isAdualt(a)));


//數據分組



//找出人數最多的班級
            Grades maxgrade =   grades.stream().max(Comparator.comparing(g -> g.getStudents().size())).get();


        }
    }

}

 

Lambda表達式

 

l  使用匿名內部類將行爲和按鈕單擊進行關聯

l  這其實是一個代碼即數據的例子——咱們給按鈕傳遞了一個表明某種行爲

的對象。

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

 

l  使用 Lambda 表達式將行爲和按鈕單擊進行關聯

button.addActionListener(event -> System.out.println("button clicked"));

 

和傳入一個實現某接口的對象不一樣,咱們傳入了一段代碼塊——一個沒有名字的函數。

event 是參數名,和上面匿名內部類示例中的是同一個參數。 -> 將參數和 Lambda 表達式

的主體分開,而主體是用戶點擊按鈕時會運行的一些代碼。

和使用匿名內部類的另外一處不一樣在於聲明 event 參數的方式。使用匿名內部類時須要顯式

地聲明參數類型 ActionEvent event ,而在 Lambda 表達式中無需指定類型,程序依然能夠

編譯。這是由於 javac 根據程序的上下文( addActionListener 方法的簽名)在後臺推斷出

了參數 event 的類型。這意味着若是參數類型不言而明,則無需顯式指定

 

 

l  編寫 Lambda 表達式的不一樣形式

l  Runnable noArguments = () -> System.out.println("Hello World"); 形式1
ActionListener oneArgument = event -> System.out.println("button clicked");形式2
Runnable multiStatement = () -> {形式3
    Lambda 表達式 |
    7
    System.out.print("Hello");
    System.out.println(" World");
};
BinaryOperator<Long> add = (x, y) -> x + y; 形式4
BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y; 形式5

 

 

Runnable noArguments = () -> System.out.println("Hello World");  ➊

 

➊所示的 Lambda 表達式不包含參數,使用空括號 () 表示沒有參數。該 Lambda 表達式實現了 Runnable 接口,該接口也只有一個 run 方法,沒有參數,且返回類型爲 void 。

 

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

 

➋中所示的 Lambda 表達式包含且只包含一個參數,可省略參數的括號,這和例 2-2 中的

形式同樣。Lambda 表達式的主體不只能夠是一個表達式,並且也能夠是一段代碼塊,使用大括號( {} )將代碼塊括起來,如➌所示

Runnable multiStatement = () -> { ➌

System.out.print("Hello");

System.out.println(" World");

};。

 

 

該代碼塊和普通方法遵循的規則別無二致,能夠用返回或拋出異常來退出。只有一行代碼的 Lambda 表達式也可以使用大括號,用以明確 Lambda表達式從何處開始、到哪裏結束。

Lambda 表達式也能夠表示包含多個參數的方法,如➍所示。

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

 

這時就有必要思考怎樣去閱讀該 Lambda 表達式。這行代碼並非將兩個數字相加,而是建立了一個函數,用來計算兩個數字相加的結果。變量 add 的類型是 BinaryOperator<Long> ,它不是兩個數字的和,而是將兩個數字相加的那行代碼。到目前爲止,全部 Lambda 表達式中的參數類型都是由編譯器推斷得出的。這固然不錯,但有時最好也能夠顯式聲明參數類型,此時就須要使用小括號將參數括起來,多個參數的狀況也是如此。如➎所示。

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

 

Lambda 表達式的類型依賴於上下文環境,是由編譯器推斷出來的

 

若是你曾使用過匿名內部類,也許遇到過這樣的狀況:須要引用它所在方法裏的變量。這

時,須要將變量聲明爲 final ,如例 2-5 所示。將變量聲明爲 final ,意味着不能爲其重複賦值。同時也意味着在使用 final 變量時,其實是在使用賦給該變量的一個特定的值。

 

例 2-5 匿名內部類中使用 final 局部變量

final String name = getUserName();

button.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent event) {

System.out.println("hi " + name);

}

});

 

 

函數接口

函數接口是隻有一個抽象方法的接口,用做 Lambda 表達式的類型

 

 

 

 

 

javac 根據 Lambda 表達式上下文信息就能推斷出參數的正確類型。程序依然要通過類型檢查來保證運行的安全性,但不用再顯式聲明類型罷了。這就是所謂的類型推斷。

 

從外部迭代到內部迭代

外部迭代:

使用 for 循環計算來自倫敦的藝術家人數

    int count = 0;
for (Artist artist : allArtists) {
        if (artist.isFrom("London")) {
            count++;
        }
    }

 

迭代過程當中的方法調用,對上面的代碼進行展開:

使用迭代器計算來自倫敦的藝術家人數

    int count = 0;
    Iterator<Artist> iterator = allArtists.iterator();
while(iterator.hasNext()) {
        Artist artist = iterator.next();
        if (artist.isFrom("London")) {
            count++;
        }
    }

 

外部迭代原理圖:

 

 

內部迭代:

該方法不是返回一個控制迭代的 Iterator 對象,而是返回內部迭代中的相應接口: Stream

使用內部迭代計算來自倫敦的藝術家人數

long count = allArtists.stream()
        .filter(artist -> artist.isFrom("London"))
        .count();

內部迭代原理圖:  Stream 是用函數式編程方式在集合類上進行復雜操做的工具

 

每種操做都對應 Stream 接口的一個方法。爲了找出來自倫敦的藝術家,須要對 Stream 對

象進行過濾: filter 。過濾在這裏是指「只保留經過某項測試的對象」。測試由一個函數完

成,根據藝術家是否來自倫敦,該函數返回 true 或者 false 。因爲 Stream API 的函數式編程風格,咱們並無改變集合的內容,而是描述出 Stream 裏的內容。 count() 方法計算給定 Stream 裏包含多少個對象

 

整個過程被分解爲兩種更簡單的操做:過濾和計數

 

只過濾,不計數

allArtists.stream().filter(artist -> artist.isFrom("London"));

 

這行代碼並未作什麼實際性的工做, filter 只刻畫出了 Stream ,但沒有產生新的集合。像

filter 這樣只描述 Stream ,最終不產生新集合的方法叫做惰性求值方法;而像 count 這樣

最終會從 Stream 產生值的方法叫做及早求值方法

 

因爲使用了惰性求值,沒有輸出藝術家的名字

allArtists.stream().filter(artist -> {

System.out.println(artist.getName());

return artist.isFrom("London");

});

若是將一樣的輸出語句加入一個擁有終止操做的流,藝術家的名字就會被輸出

 

輸出藝術家的名字

long count = allArtists.stream().filter(artist -> {

System.out.println(artist.getName());

return artist.isFrom("London");

})

.count();

這裏.count();會終止流的操做,因此會輸出藝術家的名字

 

l  判斷一個操做是惰性求值仍是及早求值很簡單:只需看它的返回值。若是返回值是 Stream ,那麼是惰性求值;若是返回值是另外一個值或爲空,那麼就是及早求值。使用這些操做的理想方式就是造成一個惰性求值的鏈,最後用一個及早求值的操做返回想要的結果

整個過程和建造者模式有共通之處。建造者模式使用一系列操做設置屬性和配置,最後調用一個 build 方法,這時,對象才被真正建立。

經常使用流的操做

List<String> collected = Stream.of("a", "b", "c") 1
.collect(Collectors.toList()); 2
    assertEquals(Arrays.asList("a", "b", "c"), collected); 3

 

首先由列表生成一個 Stream ➊,而後進行一些 Stream 上的操做,繼而是 collect 操做,由 Stream 生成列表➋,最後使用斷言判斷結果是否和預期一致➌。

 

Stream 的 of 方法使用一組初始值生成新的 Stream

 

使用 collect(toList()) 方法從 Stream 中生成一個列表,collect(toList()) 方法由 Stream 裏的值生成一個列表,是一個及早求值操做

Map 原理:

 

 

 

使用 map 操做將字符串轉換爲大寫形式

 

 

List<String> collected = Stream.of("a", "b", "hello")
            .map(string -> string.toUpperCase()) 1
.collect(Collectors.toList());
    assertEquals(asList("A", "B", "HELLO"), collected);

 

傳給 map ➊ 的 Lambda 表達式只接受一個 String 類型的參數,返回一個新的 String 。參數和返回值沒必要屬於同一種類型,可是 Lambda 表達式必須是 Function 接口的一個實例

Function 接口是隻包含一個參數的普通函數接口

 

 

使用 toCollection ,用定製的集合收集元素

 

stream.collect(toCollection(TreeSet::new));

 

 

Filter 原理圖:

 

 

filter 模式。該模式的核心思想是保留 Stream中的一些元素,而過濾掉其餘的

 

List<String> beginningWithNumbers
        = Stream.of("a", "1abc", "abc1")
        .filter(value -> isDigit(value.charAt(0)))
        .collect(Collectors.toList());

 

 

filter 接受一個函數做爲參數,該函數用 Lambda 表達式表示。該函數和前面示例中 if 條件判斷語句的功能同樣,若是字符串首字母爲數字,則返回 true 。若要重構遺留代碼, for 循環中的 if 條件語句就是一個很強的信號,可用 filter 方法替代。因爲此方法和 if 條件語句的功能相同,所以其返回值確定是 true 或者 false 。通過過濾,Stream 中符合條件的,即 Lambda 表達式值爲 true 的元素被保留下來。該 Lambda 表達式的函數接口正是前面章節中介紹過的 Predicate

 

 

 

FilterMap

flatMap 方法可用 Stream 替換值,而後將多個 Stream 鏈接成一個 Stream

filtermap 原理圖:

 

 

 

List<Integer> together = Stream.of(asList(1, 2), asList(3, 4))
        .flatMap(numbers -> numbers.stream())
        .collect(Collectors.toList());

 

調用 stream 方法,將每一個列表轉換成 Stream 對象,其他部分由 flatMap 方法處理,方法的返回值限定爲 Stream 類型

 

max 和 min

使用 Stream 查找最短曲目

List<Track> tracks = asList(new Track("Bakai", 524),
        new Track("Violets for Your Furs", 378),
        new Track("Time Was", 451));
Track shortestTrack = tracks.stream()
        .min(Comparator.comparing(track -> track.getLength()))
        .get();

 

爲了讓 Stream 對象按照曲目長度進行排序,須要傳給它一個 Comparator 對象

 

調用空 Stream 的 max 方法,返回 Optional 對象,經過調用 get 方法能夠取出 Optional 對象中的值

 

 

 

 

 

 

 

   Track shortestTrack = tracks.get(0);
for (Track track : tracks) {
        if (track.getLength() < shortestTrack.getLength()) {
            shortestTrack = track;
        }
    }

 

這段代碼先使用列表中的第一個元素初始化變量 shortestTrack ,而後遍歷曲目列表,若是

找到更短的曲目,則更新 shortestTrack ,最後變量 shortestTrack 保存的正是最短曲目。

 

 

reduce 模式

   Object accumulator = initialValue;
for(Object element : collection) {
        accumulator = combine(accumulator, element);
    }

 

首先賦給 accumulator 一個初始值: initialValue ,而後在循環體中,經過調用 combine 函

數,拿 accumulator 和集合中的每個元素作運算,再將運算結果賦給 accumulator ,最後accumulator 的值就是想要的結果

 

reduce 操做能夠實現從一組值中生成一個值。在上述例子中用到的 count 、 min 和 max 方法,由於經常使用而被歸入標準庫中。事實上,這些方法都是 reduce 操做。

 

經過 reduce 操做對 Stream 中的數字求和。以 0 做起點——一個空流 Stream 的求和結果,每一步都將 Stream 中的元素累加至 accumulator ,遍歷至 Stream 中的最後一個元素時, accumulator 的值就是全部元素的和

 

使用 reduce 操做實現累加原理圖:

 

 

 

 

 

使用 reduce 求和

有兩個參數:傳入 Stream 中的當前元素和 acc 。將兩個參數相加, acc 是累加器,保存着當前的累加結果。

int count = Stream.of(1, 2, 3)
        .reduce(0, (acc, element) -> acc + element);

 

 

展開 reduce 操做:      reduce 底層代碼

BinaryOperator<Integer> accumulator = (acc, element) -> acc + element;
int count = accumulator.apply(
        accumulator.apply(
                accumulator.apply(0, 1),
                2),
        3);

 

 

 

實際操做例子:

 

問題:找出某張專輯上全部樂隊的國籍。藝術家列表裏既有我的,也有樂隊。利用一點領域知識,假定通常樂隊名以定冠詞 The 開頭

 

思路:  1. 找出專輯上的全部表演者。

2. 分辨出哪些表演者是樂隊。

3. 找出每一個樂隊的國籍。

4. 將找出的國籍放入一個集合。

1. Album 類有個 getMusicians 方法,該方法返回一個 Stream 對象,包含整張輯中全部的

表演者;

2. 使用 filter 方法對錶演者進行過濾,只保留樂隊;

3. 使用 map 方法將樂隊映射爲其所屬國家;

4. 使用 collect(Collectors.toList()) 方法將國籍放入一個列表。

 

Set<String> origins = album.getMusicians()
        .filter(artist -> artist.getName().startsWith("The"))
        .map(artist -> artist.getNationality())
        .collect(Collectors.toSet());

 

任什麼時候候想轉化或替代代碼,都該使用 map 操做。這裏將使用比 map 更復雜的 flatMap 操做,把多個Stream 合併成一個 Stream 並返回

 

Java 的泛型是基於對泛型參數類型的擦除——換句話說,假設它是 Object 對象的實例——

所以只有裝箱類型才能做爲泛型參數。這就解釋了爲何在 Java 中想要一個包含整型值的

列表 List<int> ,實際上獲得的倒是一個包含整型對象的列表 List<Integer>

 

不一樣的函數接口有不一樣的方法。若是使用 Predicate ,就應該調用 test 方法,若是使用 Function ,就應該調用 apply 方法。

 

@FunctionalInterface  函數式接口註釋

 

 

l  三定律:

默認方法的工做原理特別是在多重繼承的下的行爲:

1. 類勝於接口。若是在繼承鏈中有方法體或抽象的方法聲明,那麼就能夠忽略接口中定義的方法

  2. 子類勝於父類。若是一個接口繼承了另外一個接口,且兩個接口都定義了一個默認方法,

那麼子類中定義的方法勝出。

  3. 沒有規則三。若是上面兩條規則不適用,子類要麼須要實現該方法,要麼將該方法聲明爲抽象方法

 

Optional 對象

 

方法引用

Lambda 表達式常常調用參數。好比想獲得藝術家的姓名,Lambda 的表達式以下:

artist -> artist.getName()

 

這種用法如此廣泛,所以 Java 8 爲其提供了一個簡寫語法,叫做方法引用,幫助程序員重

用已有方法。用方法引用重寫上面的 Lambda 表達式,代碼以下:

 

Artist::getName ;

 

標準語法爲 Classname::methodName 。須要注意的是,雖然這是一個方法,但不須要在後面加括號,由於這裏並不調用該方法

 

Artist::new

String[]::new

 

 

在一個有序集合中建立一個流時,流中的元素就按出現順序排列; 若是集合自己就是無序的,由今生成的流也是無序的。 HashSet 就是一種無序的集合

 

收集器讓流生成一個值。 maxBy 和 minBy 容許用戶按某種特定的順序生成一個值

 

 

經過調用 stream 方法讓集合生成流,而後調用 collect 方法收集結果。averagingInt 方法接受一個 Lambda 表達式做參數,將流中的元素轉換成一個整數,而後再計算平均數:

     public double averageNumberOfTracks(List<Album> albums) {

return albums.stream().collect(averagingInt(album -> album.getTrackList().size()));

}

 

 

數據分塊partitioningBy收集器:

   它接受一個流,並將其分紅兩部分,它使用 Predicate 對象判斷一個元素應該屬於哪一個部分,並根據布爾值返回一個 Map 到列表。所以,對於 true List 中的元素, Predicate 返回 true ;對其餘 List 中的元素, Predicate 返回 false 。

 

原理圖:

 

 

 

將藝術家組成的流分紅樂隊和獨唱歌手兩部分
public Map<Boolean, List<Artist>> bandsAndSolo(Stream<Artist> artists) {
    return artists.collect(partitioningBy(artist -> artist.isSolo()));
}

 

 

使用方法引用將藝術家組成的 Stream 分紅樂隊和獨唱歌手兩部分
public Map<Boolean, List<Artist>> bandsAndSoloRef(Stream<Artist> artists) {
    return artists.collect(partitioningBy(Artist::isSolo));
}

 

數據分組: groupingBy 收集器

數據分組是一種更天然的分割數據操做,與將數據分紅 ture 和 false 兩部分不一樣,可使

用任意值對數據分組。好比如今有一個由專輯組成的流,能夠按專輯當中的主唱對專輯分組

 

使用主唱對專輯分組
public Map<Artist, List<Album>> albumsByArtist(Stream<Album> albums) {
    return albums.collect(groupingBy(album -> album.getMainMusician()));
}

 

原理圖:SQL 中的 group by 操做,咱們的方法是和這相似的一個概念,只不過在 Stream 類庫中實現了而已。

 

 

 

 

 

字符串:

使用流和收集器格式化藝術家姓名
String result =
        artists.stream()
                .map(Artist::getName)
                .collect(Collectors.joining(", ", "[", "]"));

 

 

這裏使用 map 操做提取出藝術家的姓名,而後使用 Collectors.joining 收集流中的值,該方法能夠方便地從一個流獲得一個字符串,容許用戶提供分隔符(用以分隔元素)、前綴和後綴。

 

 

 

使用收集器計算每一個藝術家的專輯數

只須要對專輯計數就能夠了,核心類庫已經提供了一個這樣的收集器:counting

public Map<Artist, Long> numberOfAlbums(Stream<Album> albums) {
    return albums.collect(groupingBy(album -> album.getMainMusician(),
            counting()));
}

 

groupingBy 先將元素分紅塊,每塊都與分類函數 getMainMusician 提供的鍵值相關聯,然

後使用下游的另外一個收集器收集每塊中的元素

 

 

 

StringBuilder builder = new StringBuilder("[");
artists.stream()
        .map(Artist::getName)
        .forEach(name -> {
            if (builder.length() > 1)
                builder.append(", ");
            builder.append(name);
        });
builder.append("]");
String result = builder.toString();

 

將上面代碼進行優化:

StringBuilder reduced =
        artists.stream()
                .map(Artist::getName)
                .reduce(new StringBuilder(), (builder, name) -> {
                    if (builder.length() > 0)
                        builder.append(", ");
                    builder.append(name);
                    return builder;
                }, (left, right) -> left.append(right));
reduced.insert(0, "[");
reduced.append("]");
String result = reduced.toString();

相關文章
相關標籤/搜索