上一篇咱們介紹了Strem的概念與實際的一些操做,本篇咱們繼續來學習Stream的另外一個重要操做,分組與分區。
咱們在上一篇介紹Stream的操做時,會常常使用到Collectors這個類,這個類其實是一個封裝了不少經常使用的匯聚操做的一個工廠類。咱們以前用到過sql
//將結果匯聚到ArrayList中 Collectors.toList();
//將結果匯聚到HashSet中 Collectors.toSet();
以及更爲通用的segmentfault
//將結果匯聚到一個指定類型的集合中 Collectors.toCollection(Supplier<C> collectionFactory);
Stream分組app
在實際開發中,對於將一個集合的內容進行分組或分區這種需求也很是常見,因此咱們繼續學習下Collectors類中的groupingBy和partitioningBy方法。函數
public static Collector groupingBy(Function<? super T, ? extends K> classifier){ //... }
groupingBy接收一個Function類型的變量classifier,classifier被稱做分類器,收集器會按着classifier做爲key對集合元素進行分組,而後返回Collector收集器對象,假如如今有一個實體Student學習
public class Student { private String name; private int score; private int age; public Student(String name,int score,int age){ this.name = name; this.score = score; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
咱們如今按Student的name進行分組,若是使用sql來表示就是select * from student group by name; 再看下使用Stream的方式this
Map<String, List<Student>> collect = students.stream().collect(Collectors.groupingBy(Student::getName));
這裏咱們使用方法引用(類名::實例方法名)替代lambda表達式(s -> s.getName())的方式來指定classifier分類器,使集合按Student的name來分組。
注意到分組後的返回類型是Map<String, List<Student>>,結果集中會將name做爲key,對應的Student集合做爲value返回。
那若是按name分組後,想求出每組學生的數量,就須要藉助groupingBy另外一個重載的方法code
public static Collector groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream){ //... }
第二個參數downstream仍是一個收集器Collector對象,也就是說咱們能夠先將classifier做爲key進行分組,而後將分組後的結果交給downstream收集器再進行處理對象
//按name分組 得出每組的學生數量 使用重載的groupingBy方法,第二個參數是分組後的操做 Map<String, Long> collect1 = students.stream().collect(Collectors.groupingBy(Student::getName, Collectors.counting()));
Collectors類這裏也幫咱們封裝好了用於統計數量的counting()方法,這裏先了解一下counting()就是將收集器中元素求總數便可,後續咱們會再深刻源碼學習。接口
咱們還能夠對分組後的數據求平均值源碼學習
Map<String, Double> collect2 = students.stream().collect(Collectors.groupingBy(Student::getName, Collectors.averagingDouble(Student::getScore)));
averagingDouble方法接收一個ToDoubleFunction參數
@FunctionalInterface public interface ToDoubleFunction<T> { /** * Applies this function to the given argument. * * @param value the function argument * @return the function result */ double applyAsDouble(T value); }
ToDoubleFunction實際上也是Function系列函數式接口中的其中一個特例,接收一個參數,返回Double類型(這裏是接收一個Student返回score)。由於分組後的集合中每一個元素是Student類型的,因此咱們沒法直接對Student進行求平均值
//僞代碼 Collectors.averagingDouble(Student))
因此須要將Student轉成score再求平均值,Collectors.averagingDouble(Student::getScore))。
Stream分區
針對上面的Student,咱們如今再加一個需求,分別統計一下及格和不及格的學生(分數是否>=60)
這時候符合Stream分區的概念了,Stream分區會將集合中的元素按條件分紅兩部分結果,key是Boolean類型,value是結果集,知足條件的key是true,咱們看下示例。
Map<Boolean, List<Student>> collect3 = students.stream().collect(Collectors.partitioningBy(student -> student.getScore() >= 60)); System.out.println(collect3.get(true));//輸出及格的Student System.out.println(collect3.get(false));//輸出不及格的Student
partitioningBy方法接收一個Predicate做爲分區判斷的依據,知足條件的元素放在key爲true的集合中,反之放在key爲false的集合中
//partitioningBy方法 public static Collector partitioningBy(Predicate<? super T> predicate) { return partitioningBy(predicate, toList()); }