2015年11月7日 星期六

Java的Stream.reduce()

Java SE8 技術手冊(好書一本) 12-35頁有段程式碼是這頁樣寫的
int sum = employees.stream()
          .filter(employee ->employee.getGender() == Gender2.MALE)
          .mapToInt(Employee2::getAge)
          .reduce((total,age) -> total + age)
          .getAsInt();
該書還有句話是這麼說的,若是reduce()沒有指定初值,就會試著使用該組數據中第一個元素作為第一次呼叫Lambda表示式時的第一個引數值
老實說我第一次看到就想說這樣第一的值不是被重複計算了嗎?
(第一個引數是上次運算結果,第二個引數是目前走訪的元素)
所以查了一下API文件發現,reduce()的作用大概同等於(但不限制為按順序執行。):
     boolean foundAny = false;
     T result = null;
     for (T element : this stream) {
         if (!foundAny) {
             foundAny = true;
             result = element;
         }
         else
             result = accumulator.apply(result, element);
     }
     return foundAny ? Optional.of(result) : Optional.empty();
所以大概可以想成 第一個引數直接指定成該組數據中的第一個元素吧! (如果有的話)
我寫了一個測試程式發現 迭代的第一次確實沒有進去執行reduce中的lambda表示式。
(或許應該說第一次執行時就將該組數據中第一個元素當成lambda表示式的第一個引數,第二個元素當成lambda表示式的第二個引數。)
package learning.test;

import java.util.Arrays;
import java.util.List;

public class StreamTest {
 public static void main(String[] args) {
  List&ltEmployee&gt employees = Arrays.asList(new Employee(10),
    new Employee(20), new Employee(30), new Employee(50));
  int sum = employees.stream().filter(employee -&gt employee.getAge() &gt 10)
    .mapToInt(Employee::getAge).reduce((total, age) -&gt {
     System.out.println("age=" + age);
     System.out.println("total=" + total);
     System.out.println("-----------");
     return total + age;
    }).getAsInt();
  System.out.println(sum);
 }
}

class Employee {
 int age;

 Employee(int a) {
  age = a;
 }

 public int getAge() {
  return age;
 }
}
輸出結果:
age=30
total=20
-----------
age=50
total=50
-----------
100

沒有留言:

張貼留言