Java 8 - 利用Lambda與Stream來改寫Collection的進階操作

繼前面提到的基本操作之後,在這邊要討論的部分是如何利用Stream reference搭配Lambda表

式示的語法,再加上Collectors類別下的groupingBy method將List下的類別物件進行分組的動

作,如此一來可以達到類似SQL語法下做group by針對某些欄位分組的行為。

首先,一樣套用前面基本操作文章有提到的Transcript class來建立一List,並且多定義了一

成員變數sex,來針對每一筆Transcript能區別出為Male or Female。

class Transcript{
    ....
    private String sex;
    ...
    public Transcript(int id, String name, String sex, int chinese, int math, int english)     {
       super();
       this.id = id;
       this.sex = sex;
       ...
    }

    ....
    public String getSex() { 
       return sex;
    }
    public void setSex(String sex) {
       this.sex = sex;
    }
}

基本建置語法改寫如下:
List<Transcript> studentList = new ArrayList<Transcript>();
Transcript t1 = new Transcript(2,"Ben", "Male", 87, 91, 80);
Transcript t2 = new Transcript(1,"Sjkok", "Female", 94, 99, 65);
Transcript t3 = new Transcript(3,"Aaron", "Male", 44, 55, 100);
Transcript t4 = new Transcript(4,"Zhibin", "Male", 77, 88, 66);
studentList.add(t1);
studentList.add(t2);
studentList.add(t3);
studentList.add(t4);

多定義了一個性別屬性,來達到做分組的示範。

#單一條件群組的使用

1. Collectors.groupingBy使用

再來,基本的群組的使用語法如下:

Map<String, List<Transcript>> transBySex = null;
transBySex = studentList.stream()
                        .collect(Collectors.groupingBy(Transcript::getSex));

說明如下:

一樣利用stream reference使用collect,此時在參數的地方assign的是Collectors.groupingBy

,其將針對stream reference所擁有的Transcript物件進行分組,而分組的條件為

Transcript::getSex。在Java多載之下,因應collect所帶入的參數在此時為回傳Map型態物件

,並且在這邊依據groupingBy的型態對應至Map的key也為String型態,而value不諱言應該

就是List<Transcript>了,因為sex可能對應至多個Transcript物件。

PS. Transcript::getSex為Lambda語法的簡寫型式,相當於Transcript t -> t.getSex()

2. Collectors.joining使用

transBySex.forEach((sex, trans) -> {
   System.out.println(sex+"=>"+
      trans.stream()
           .map(t -> t.getName())
           .collect(Collectors.joining(", ", "[", "]")));
});

在得到一回傳分組物件Map<String, List<Transcript>>後,接下來就是進行print的動作了。

首先,在這邊stream reference使用map的目的為,將Transcript object轉換成String,且只有

針對Name的部分。

PS. 在此只是要示範出,Male or Female的部分各別為哪一些人!

再來,當您取出Name之後可以怎麼陳列呢?  在一個群組有多個Name的當下,利用join來達

到多個字串的串接,並且這個method有個方便的設計,不只是針對要串接的符號為啥,

還可以針對頭跟尾設定串接哪個字串來達到使用join的完整性!

PS. 相信有使用過JavaScript的人大概都知道join可以達到什麼功用囉!

#多個條件群組的使用

在這邊要示範的是,在分組的時候,針對該分組能夠達到得進一步設定,在此為以sex分組

後,如何針對該分組下的單一科目分數取得大家的平均值! 主要是需設定一downstream。

Java 8 API說明如下:



語法使用如下:
Map<String, Double> avgMathBySex = null;
avgMathBySex = studentList.stream()
                          .collect(Collectors.groupingBy(Transcript::getSex,
                                   Collectors.averagingInt(Transcript::getMath)));
avgMathBySex.forEach((sex , avgmath) -> System.out.println(sex+"=>"+avgmath));

在此另一參數,會回傳該分組下的Transcript物件下某一屬性的平均值,至於是哪個屬性,

在此指定為Math,因此最後collect會依據collect內的參數回傳型態來推斷出Map對應的

key為String及value為Double。

PS. Collectors.averagingInt計算後回傳的數值為Double並非Integer!

最後,所有範例列印的結果:


參考資料   Java 8 Lamdas技術手冊

留言