聚合操做一節描述了下列操做管道,計算集合roster
中全部男性成員的平均年齡:html
double average = roster .stream() .filter(p -> p.getGender() == Person.Sex.MALE) .mapToInt(Person::getAge) .average() .getAsDouble();
JDK包含許多終端操做(好比average、sum、min、max和count),它們經過組合流的內容返回一個值,這些操做稱爲概括操做。JDK還包含返回一個集合而不是單個值的概括操做,許多概括操做執行特定的任務,例如查找值的平均值或將元素分組到類別中。可是,JDK爲你提供了通用的reduce和collect操做,本節將詳細介紹這些操做。java
你能夠在示例ReductionExamples中找到本節中描述的代碼摘錄。git
Stream.reduce方法是一種通用的概括操做,考慮如下管道,它計算集合roster
中男性成員的年齡總和,它使用Stream.sum概括操做。github
Integer totalAge = roster .stream() .mapToInt(Person::getAge) .sum();
將其與下面使用流的管道進行比較,使用Stream.reduce
操做計算相同的值:segmentfault
Integer totalAgeReduce = roster .stream() .map(Person::getAge) .reduce( 0, (a, b) -> a + b);
本例中的reduce
操做有兩個參數:api
identity
:標識元素既是概括的初始值,也是流中沒有元素時的默認結果,在本例中,標示元素爲0
,這是年齡總和的初始值,若是集合roster
中沒有成員,則爲默認值。accumulator
:累加器函數接受兩個參數:概括的部分結果(在本例中,是到目前爲止全部處理過的整數的和)和流的下一個元素(在本例中,是一個整數),它返回一個新的局部結果。在本例中,累加器函數是一個lambda表達式,它添加兩個Integer
值並返回一個Integer
值:(a, b) -> a + b
。reduce
操做老是返回一個新值,可是,accumulator
函數每次處理流的元素時也會返回一個新值,假設你但願將流的元素概括爲更復雜的對象,例如集合,這可能會影響應用程序的性能。若是reduce
操做涉及向集合添加元素,那麼每次累加器函數處理一個元素時,它都會建立一個包含該元素的新集合,這是低效的,相反,更新現有的集合將更有效,你可使用Stream.collect來實現這一點。與reduce
方法不一樣,collect
方法修改或改變現有值,而reduce
方法在處理元素時老是建立一個新值。oracle
考慮如何找到流中值的平均值,你須要兩段數據:值的總數和這些值的和。然而,與reduce
方法和全部其餘概括方法同樣,collect
方法只返回一個值,你能夠建立一個包含成員變量的新數據類型,該成員變量跟蹤值的總數和這些值的總和,例以下面的類Averager:app
class Averager implements IntConsumer { private int total = 0; private int count = 0; public double average() { return count > 0 ? ((double) total)/count : 0; } public void accept(int i) { total += i; count++; } public void combine(Averager other) { total += other.total; count += other.count; } }
下面的管道使用Averager
類和collect
方法計算全部男性成員的平均年齡:ide
Averager averageCollect = roster.stream() .filter(p -> p.getGender() == Person.Sex.MALE) .map(Person::getAge) .collect(Averager::new, Averager::accept, Averager::combine); System.out.println("Average age of male members: " + averageCollect.average());
本例中的collect
操做接受三個參數:函數
supplier
:supplier
是個工廠方法,它構造新的實例,對於collect
操做,它建立結果容器的實例,在本例中,它是Averager
類的一個新實例。accumulator
:accumulator
函數將流元素合併到結果容器中,在本例中,它經過將count
變量增長1,並將流元素的值添加到total
成員變量中,該元素是一個整數,表示男性成員的年齡,來修改Averager
結果容器。combiner
:combiner
函數接受兩個結果容器併合並它們的內容,在本例中,它經過將count
變量與另外一個Averager
實例的count
成員變量相加,並將另外一個Averager
實例的total
成員變量的值添加到total
成員變量中,從而修改Averager
結果容器。請注意如下:
supplier
是lambda表達式(或方法引用),而不是reduce
操做中的identity
元素之類的值。accumulator
和combiner
函數不返回值。collect
操做(若是你使用並行流運行collect
方法,那麼每當combiner
函數建立一個新對象時,JDK都會建立一個新線程,例如本例中的Averager
對象,所以,你沒必要擔憂同步)。雖然JDK提供了計算流中元素平均值的average
操做,可是若是須要從流的元素中計算多個值,可使用collect
操做和自定義類。
collect
操做最適合於集合,下面的示例使用collect
操做將男性成員的名稱放入集合中:
List<String> namesOfMaleMembersCollect = roster .stream() .filter(p -> p.getGender() == Person.Sex.MALE) .map(p -> p.getName()) .collect(Collectors.toList());
這個版本的collect
操做只接受Collector類型的一個參數,該類封裝了collect
操做中用做參數的函數,該操做須要三個參數(supplier
、accumulator
和combiner
函數)。
Collectors類包含許多有用的概括操做,好比將元素累積到集合中,並根據各類標準彙總元素,這些概括操做返回類Collector
的實例,所以能夠將它們用做collect
操做的參數。
本例使用Collectors.toList操做,它將流元素累積到List
的新實例中,與Collectors
類中的大多數操做同樣,toList
操做符返回Collector
的實例,而不是集合。
如下示例按性別將集合roster
的成員分組:
Map<Person.Sex, List<Person>> byGender = roster .stream() .collect( Collectors.groupingBy(Person::getGender));
groupingBy操做返回一個map,其鍵是應用指定爲其參數的lambda表達式(稱爲分類函數)所獲得的值。在本例中,返回的map包含兩個鍵,Person.Sex.MALE
和Person.Sex.FEMALE
,鍵對應的值是List
的實例,其中包含流元素,當分類函數處理這些元素時,這些元素與鍵值對應。例如,與鍵Person.Sex.MALE
對應的值是一個包含全部男性成員的List
實例。
如下示例檢索集合roster
中每一個成員的姓名,並按性別將其分組:
Map<Person.Sex, List<String>> namesByGender = roster .stream() .collect( Collectors.groupingBy( Person::getGender, Collectors.mapping( Person::getName, Collectors.toList())));
本例中的groupingBy操做接受兩個參數,一個分類函數和一個Collector
實例,Collector
參數稱爲下游收集器,這是Java運行時應用於另外一個收集器的結果的收集器。所以,這個groupingBy
操做使你可以對groupingBy
操做符建立的List
值應用collect
方法。此示例應用收集器mapping,它將mapping
函數Person::getName
應用於流的每一個元素。所以,產生的流只包含成員的名稱,包含一個或多個下游收集器的管道(如本例)稱爲多級概括。
下面的示例檢索每種性別成員的總年齡:
Map<Person.Sex, Integer> totalAgeByGender = roster .stream() .collect( Collectors.groupingBy( Person::getGender, Collectors.reducing( 0, Person::getAge, Integer::sum)));
reducing操做須要三個參數:
identity
:如Stream.reduce
操做,若是流中沒有元素,則identity
元素既是概括的初始值,也是缺省結果,在這個例子中,identity
元素是0
,這是年齡總和的初始值,若是不存在成員,則爲默認值。mapper
:reducing
操做將此mapper
函數應用於全部流元素,在本例中,mapper
檢索每一個成員的年齡。operation
:operation
函數用於概括映射值,在本例中,operation
函數添加Integer
值。下面的例子檢索了每種性別成員的平均年齡:
Map<Person.Sex, Double> averageAgeByGender = roster .stream() .collect( Collectors.groupingBy( Person::getGender, Collectors.averagingInt(Person::getAge)));