Java 8 - 收集器Collectors_归约和汇总
文章目錄
- Pre
- 查找流中的最大值和最小值
- 需求:想要找出熱量最高的菜和熱量最低的菜
- 匯總
- 需求: 求出菜單列表的總熱量
- 需求: 一次操作求出菜單中元素的個數,并得總和、平均值、最大值和最小值 (summarizingXXX)
- 連接字符串
- 需求 :把菜單中所有菜肴的名稱連接起
Pre
在需要將流項目重組成集合時,一般會使用收集器( Stream 方法 collect的參數)。再寬泛一點來說,但凡要把流中所有的項目合并成一個結果時就可以用。這個結果可以是任何類型。
public static List<Dish> menu = Arrays.asList(new Dish("pork", false, 800, Dish.Type.MEAT),new Dish("beef", false, 700, Dish.Type.MEAT),new Dish("chicken", false, 400, Dish.Type.MEAT),new Dish("french fries", true, 530, Dish.Type.OTHER),new Dish("rice", true, 350, Dish.Type.OTHER),new Dish("season fruit", true, 120, Dish.Type.OTHER),new Dish("pizza", true, 550, Dish.Type.OTHER),new Dish("prawns", false, 300, Dish.Type.FISH),new Dish("salmon", false, 450, Dish.Type.FISH));來看個簡單的需求: 利用 counting 工廠方法返回的收集器,統計菜單中有多少種菜
public static Long howManyDishes(List<Dish> menu) {// return menu.stream().count();return menu.stream().collect(Collectors.counting());}還可以寫得更為直接:
return menu.stream().count();查找流中的最大值和最小值
需求:想要找出熱量最高的菜和熱量最低的菜
public static Optional<Dish> highCa(List<Dish> menu) {Optional<Dish> collect = menu.stream().collect(Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)));return collect;}public static Optional<Dish> lowC(List<Dish> menu) {Optional<Dish> collect = menu.stream().collect(Collectors.minBy(Comparator.comparing(Dish::getCalories)));return collect;}可以使用兩個收集器, Collectors.maxBy 和Collectors.minBy ,來計算流中的最大或最小值。這兩個收集器接收一個 Comparator 參數來比較流中的元素。
可以創建一個 Comparator 來根據所含熱量對菜肴進行比較,并把它傳遞給Collectors.maxBy :
Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);然后進行計算
Optional<Dish> collect = menu.stream().collect(Collectors.maxBy(dishCaloriesComparator ));Optional<Dish> 是怎么回事。要回答這個問題,我們需要問“要是 menu 為空怎么辦”。那就沒有要返回的?了!Java 8引入了 Optional ,它是一個容器,可以包含也可以不包含值。這里它完美地代表了可能也可能不返回菜肴的情況。
匯總
另一個常見的返回單個值的歸約操作是對流中對象的一個數值字段求和、求平均數等等。這種操作被稱為匯總操作。讓我們來看看如何使用收集器來表達匯總操作。
Collectors 類專門為匯總提供了一個工廠方法: Collectors.summingInt 。它可接受一 個把對象映射為求和所需 int 的函數,并返回一個收集器;該收集器在傳遞給普通的 collect 方法后即執行我們需要的匯總操作。
需求: 求出菜單列表的總熱量
public static Integer allCal(List<Dish> menu) {Integer collect = menu.stream().collect(Collectors.summingInt(Dish::getCalories));return collect;}收集過程如下:
在遍歷流時,會把每一道菜都映射為其熱量,然后把這個數字累加到一個累加器(這里的初始值 0 )。
Collectors.summingLong 和 Collectors.summingDouble 方法的作用完全一樣,可以用于求和字段為 long 或 double 的情況。
但匯總不僅僅是求和;還有 Collectors.averagingInt ,連同對應的 averagingLong 和averagingDouble 可以計算數值的平均數:
public static Double avg(List<Dish> menu) {Double collect = menu.stream().collect(Collectors.averagingInt(Dish::getCalories));return collect;}截止到現在,我們使用收集器來給流中的元素計數,找到這些元素數值屬性的最大值和最小值,以及計算其總和和平均值。
需求: 一次操作求出菜單中元素的個數,并得總和、平均值、最大值和最小值 (summarizingXXX)
public static IntSummaryStatistics sumInfo(List<Dish> menu) {IntSummaryStatistics collect = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));return collect;}輸出
summarizingInt這個收集器會把所有這些信息收集到一個叫作 IntSummaryStatistics 的類里,它提供了方便的取值(getter)方法來訪問結果。
同樣,相應的 summarizingLong 和 summarizingDouble 工廠方法有相關的 LongSummary-Statistics 和 DoubleSummaryStatistics 類型,適用于收集的屬性是原始類型 long 或double 的情況。
連接字符串
joining 工廠方法返回的收集器會把對流中每一個對象應用 toString 方法得到的所有字符串連接成一個字符串。
需求 :把菜單中所有菜肴的名稱連接起
public static String joinMenu(List<Dish> menu) {return menu.stream().map(Dish::getName).collect(Collectors.joining());}請注意, joining 在內部使用了 StringBuilder 來把生成的字符串逐個追加起來。
此外還要注意,如果 Dish 類有一個 toString 方法來返回菜肴的名稱,那你無需用提取每一道菜名稱的函數來對原流做映射就能夠得到相同的結果。
String shortMenu = menu.stream().collect(joining());但該字符串的可讀性并不好。幸好, joining 工廠方法有一個重載版本可以接受元素之間的分界符,這樣你就可以得到一個指定分隔符的名稱列表:
public static String joinMenu(List<Dish> menu) {return menu.stream().map(Dish::getName).collect(joining(","));}好了 ,就到這兒吧
總結
以上是生活随笔為你收集整理的Java 8 - 收集器Collectors_归约和汇总的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 8 - 收集器Collecto
- 下一篇: Java 8 - 收集器Collecto