Java 8 - Stream flatMap的使用方式

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

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

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

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

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

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

  1. class Sclass{
  2. private String sname;
  3. private List<Transcript> transcript;
  4. public Sclass(String sname, List<Transcript> transcript) {
  5. super();
  6. this.sname = sname;
  7. this.transcript = transcript;
  8. }
  9. public String getSname() {
  10. return sname;
  11. }
  12. public List<Transcript> getTranscript() {
  13. return transcript;
  14. }
  15. }
  16. class Transcript implements Cloneable{
  17. ...
  18. ...
  19. public Transcript(int id, String name, String sex, int chinese, int math, int english) {
  20. super();
  21. this.id = id;
  22. this.sex = sex;
  23. this.name = name;
  24. this.chinese = chinese;
  25. this.math = math;
  26. this.english = english;
  27. }
  28. ...
  29. ...
  30. public void setName(String name) {
  31. this.name = name;
  32. }
  33. public Transcript clone(){
  34. Transcript transcript = null;
  35. try {
  36. transcript = (Transcript) super.clone();
  37. } catch (CloneNotSupportedException e) {
  38. // TODO Auto-generated catch block
  39. e.printStackTrace();
  40. }
  41. return transcript;
  42. }
  43. }
  44. public class StreamExample {
  45. public static void main(String[] args) {
  46. // TODO Auto-generated method stub
  47. List<Transcript> studentList = new ArrayList<Transcript>();
  48. Transcript t1 = new Transcript(2,"Ben", "Male", 87, 91, 80);
  49. Transcript t2 = new Transcript(1,"Sjkok", "Female", 94, 99, 65);
  50. Transcript t3 = new Transcript(3,"Aaron", "Male", 44, 55, 100);
  51. Transcript t4 = new Transcript(4,"Zhibin", "Male", 77, 88, 66);
  52. studentList.add(t1);
  53. studentList.add(t2);
  54. studentList.add(t3);
  55. studentList.add(t4);
  56. System.out.println("=======================================");
  57. Sclass sclass[] = new Sclass[2];
  58. sclass[0] = new Sclass("ClassA", studentList);
  59. List<Transcript> studentListB = studentList.stream()
  60. .map(t -> {
  61. Transcript tc = t.clone();
  62. tc.setName(tc.getName()+"*");
  63. return tc;
  64. })
  65. .collect(Collectors.toList());
  66. sclass[1] = new Sclass("ClassB", studentListB);
  67. }
  68. }

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

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

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

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

加上*以做區隔!

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

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

一、沒使用flatMap

  1. Set<String> student = new HashSet<String>();
  2. System.out.println("使用forEach");
  3. Stream.of(sclass)
  4. .forEach(s -> {
  5. s.getTranscript().stream()
  6. .filter(t -> t.getEnglish() > 70)
  7. .map(t -> t.getName())
  8. .forEach(name -> student.add(name));
  9. });
  10. 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
  1. student.clear();
  2. System.out.println("使用flatMap");
  3. Stream.of(sclass)
  4. .flatMap(s -> s.getTranscript().stream())
  5. .filter(t -> t.getEnglish() > 70)
  6. .map(t -> t.getName())
  7. .forEach(name -> student.add(name));
  8. System.out.println(student);

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

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

做filter等接下來的行為!

PS. 經過flatMap之後原本是

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

最後輸出的結果如下:


參考資料   Java 8 Lamdas技術手冊

留言