Java 8 - Stream flatMap的使用方式

之前在撰寫Java 8 Lambda表示式及利用Stream改寫現有Collection的操作時,漏掉flatMap部分

。後來又翻閱了一下書籍發現flatMap的應用能夠串接Stream省略forEach的使用。因此,自己

也寫個例子來記錄一下使用方式!

之前寫的Java 8 - 利用Lambda與Stream來改寫Collection的基本操作 有帶到Lambda語法簡單

使用的說明與成績單範例。承襲這個例子,小弟除了改寫了一下Transcript class(成績單),並

且增加了一Sclass class(班級)的使用。相關語法如下:

class Sclass{
 private String sname;
 private List<Transcript> transcript;
 public Sclass(String sname, List<Transcript> transcript) {
  super();
  this.sname = sname;
  this.transcript = transcript;
 }
 public String getSname() {
  return sname;
 }
 public List<Transcript> getTranscript() {
  return transcript;
 }
}

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

public Transcript clone(){
  Transcript transcript = null;
  try {
   transcript = (Transcript) super.clone();
  } catch (CloneNotSupportedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return transcript;
 }

}

public class StreamExample {
   public static void main(String[] args) {
      // TODO Auto-generated method stub  
      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);

      System.out.println("=======================================");
      Sclass sclass[] = new Sclass[2];
      sclass[0] = new Sclass("ClassA", studentList);
      List<Transcript> studentListB = studentList.stream()
                                                 .map(t -> {
                                                        Transcript tc = t.clone();
                                                        tc.setName(tc.getName()+"*");
                                                        return tc;
                                                 })
                                                .collect(Collectors.toList());
      sclass[1] =  new Sclass("ClassB", studentListB);
   }
}

首先,一樣先建置Transcript的studentList List

再來定義一Sclass的物件陣列來表示處理兩個班級的成績單

因為要有兩個班級的資料,因此在Transcript內定義clone method來做淺複製的動作,由於

懶得再幫ClassB建立另外一組資料。在做clone的當下,針對複製後的物件在name的屬性上

加上*以做區隔!

基本的資料都備妥了之後,接下來就是要做資料的篩選動作

題目:找出兩個班級內English分數大於70的同學

一、沒使用flatMap

Set<String> student = new HashSet<String>();
System.out.println("使用forEach");
Stream.of(sclass)
      .forEach(s -> {
         s.getTranscript().stream()
                          .filter(t -> t.getEnglish() > 70)
                          .map(t -> t.getName())
                          .forEach(name -> student.add(name));
      });
System.out.println(student);

在此將sclass array轉換成stream以便呼叫相關method操作

由於要針對多個班級下的成績單做處理,因此至少需要兩層迴圈的動作

第一層forEach為走訪班級,取出Sclass object後呼叫getTranscript(),再來轉換成stream以便接

下來做filter來過濾掉條件不成立的集合內的Transcript,再來透過map method來將當下集合

型態由Transcript轉換成String,因此最後透過forEach將符合的名字記錄在student變數內!

PS. forEach回傳的是void,因此student變數在內部做add動作!

二、使用flatMap
student.clear();
System.out.println("使用flatMap");
Stream.of(sclass)
      .flatMap(s -> s.getTranscript().stream())
      .filter(t -> t.getEnglish() > 70)
      .map(t -> t.getName())
      .forEach(name -> student.add(name));
System.out.println(student);

同上一個方式,差別只在於第一層迴圈的走訪被省略掉了,在班級走訪的部分改成flatMap

在此是當下走訪完Sclass陣列並且將兩個班級的Transcript stream做串接的動作,再一次去

做filter等接下來的行為!

PS. 經過flatMap之後原本是

Stream<Transcript> s1與Stream<Transcript> s2 => Stream<Transcript> sTotal

最後輸出的結果如下:


參考資料   Java 8 Lamdas技術手冊

留言