Java8之用流收集數據

//菜單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) );

1 流中最大值最小值

Comparator<Dish> dishComparator = Comparator.comparingInt(Dish::getCalories);//最大值最小值Optional<Dish> maxCalorieDish = menu.stream().collect(Collectors.maxBy(dishComparator));Optional<Dish> minCalorieDish = menu.stream().collect(Collectors.minBy(dishComparator));

2 彙總(求和、平均數)

//求和
int totalCalories = menu.stream().collect(Collectors.summingInt(Dish::getCalories));
int calories = menu.stream().mapToInt(Dish::getCalories).sum();
int sum = menu.stream().map(Dish::getCalories).reduce(0, Integer::sum);
totalCalories = menu.stream().collect(Collectors.reducing(0, Dish::getCalories, (i, j) -> i + j));
//平均數
double avgCalories = menu.stream().collect(Collectors.averagingInt(Dish::getCalories));

//通過一次summarizing操作你可以就數出菜單中元素的個數,並得到菜餚熱量總和、平均值、最大值和最小值
IntSummaryStatistics menuStatistics = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));
//取值
menuStatistics.getCount();
menuStatistics.getAverage();
menuStatistics.getMax();
menuStatistics.getMin();

3 連接字符串

joining工廠方法返回的收集器會把對流中每一個對象應用toString方法得到的所有字符串連接成一個字符串。

String shortMenu = menu.stream().map(Dish::getName).collect(joining());
//可以接受元素之間的分界符,這樣你就可以得到一個逗號分隔的菜餚名稱列表
shortMenu = menu.stream().map(Dish::getName).collect(joining(","));

注意:在內部使用了StringBuilder來把生成的字符串逐個追加起來

4 廣義的歸約彙總

totalCalories = menu.stream().collect(Collectors.reducing(0, Dish::getCalories, (i, j) -> i + j));

//
totalCalories = menu.stream().collect(Collectors.reducing(0,Dish::getCalories,Integer::sum));
totalCalories = menu.stream().map(Dish::getCalories).reduce(Integer::sum).get();
//最好的求和方法,IntStream可以讓我們避免自動拆箱操作,也就是從Integer到int的隱式轉換
totalCalories = menu.stream().mapToInt(Dish::getCalories).sum();

它需要三個參數:

  1. 第一個參數是歸約操作的起始值,也是流中沒有元素時的返回值,所以很顯然對於數值和而言0是一個合適的值。
  2. 第二個參數就是你是使用的函數,將菜餚轉換成一個表示其所含熱量的int。
  3. 第三個參數是一個BinaryOperator,將兩個項目累積成一個同類型的值。這裏它就是對兩個int求和。
Optional<Dish> mostCalorieDish = menu.stream().collect(Collectors.reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2));

5 分組

Map<Dish.Type, List<Dish>> dishesByType = menu.stream().collect(Collectors.groupingBy(Dish::getType));
//如果你無法通過引用方法來得到分類,如把熱量不到400卡路里的菜劃分爲“低熱量”(diet),熱量400到700
//卡路里的菜劃爲“普通”(normal),高於700卡路里的劃爲“高熱量”(fat)
public enum CaloricLevel { DIET, NORMAL, FAT }

Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(Collectors.groupingBy(dish -> {
                    if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                    else if (dish.getCalories() <= 700) return
                            CaloricLevel.NORMAL;
                    else return CaloricLevel.FAT;
                } ));

5.1 多級分組

//二級分組
Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel = menu.stream().collect(    Collectors.groupingBy(Dish::getType,            Collectors.groupingBy(dish -> {                if (dish.getCalories() <= 400) return CaloricLevel.DIET;                else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;                else return CaloricLevel.FAT;            } )    ));

5.2 按子組收集數據

//要數一數菜單中每類菜有多少個
Map<Dish.Type, Long> typesCount = menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.counting()));
//以Dish的類型作爲鍵,以包裝了該類型中熱量最高的Dish的Optional<Dish>作爲
Map<Dish.Type, Optional<Dish>> mostCaloricByType =
                menu.stream()
                        .collect(Collectors.groupingBy(Dish::getType,
                                Collectors.maxBy(comparingInt(Dish::getCalories))));

把收集器的結果轉換爲另一種類型

Map<Dish.Type, Dish> mostCaloricByType2 =
                menu.stream()
                        .collect(Collectors.groupingBy(Dish::getType,
                                collectingAndThen(
                                        maxBy(comparingInt(Dish::getCalories)),
                                        Optional::get)));

與groupingBy聯合使用的其他收集器的例子

Map<Dish.Type, Integer> totalCaloriesByType =        menu.stream().collect(Collectors.groupingBy(Dish::getType,                summingInt(Dish::getCalories)));

mapping接受兩個參數:一個函數對流中的元素做變換,另一個則將變換的結果對象收集起來。

對於每種類型的Dish,菜單中都有哪些CaloricLevel

 Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType =
                menu.stream().collect(
                        groupingBy(Dish::getType, mapping(
                                dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                                else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                                else return CaloricLevel.FAT; },
                                toSet() )));
Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType2 =
                menu.stream().collect(
                        groupingBy(Dish::getType, mapping(
                                dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                                else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                                else return CaloricLevel.FAT; },
                                toCollection(HashSet::new) )));

6 分區

分區是分組的特殊情況:由一個謂詞(返回一個布爾值的函數)作爲分類函數,它稱分區函數。分區函數返回一個布爾值,這意味着得到的分組Map的鍵類型是Boolean,於是它最多可以分爲兩組——true是一組,false是一組。

如果你是素食者或是請了一位素食的朋友來共進晚餐,可能會想要把菜單按照素食和非素食分開。

Map<Boolean, List<Dish>> partitionedMenu = menu.stream().collect(partitioningBy(Dish::isVegetarian));
//那麼通過Map中鍵爲true的值,就可以找出所有的素食菜餚了:
List<Dish> vegetarianDishes = partitionedMenu.get(true);

//方法二
List<Dish> vegetarianDishes2 = menu.stream().filter(Dish::isVegetarian).collect(toList());

分區後分組收集器

//對分區後的內容進行分組
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =
menu.stream().collect(
partitioningBy(Dish::isVegetarian,
groupingBy(Dish::getType)));
//找出素食和非素食中熱量最大的菜
Map<Boolean, Dish> mostCaloricPartitionedByVegetarian =
menu.stream().collect(
partitioningBy(Dish::isVegetarian,
collectingAndThen(
maxBy(comparingInt(Dish::getCalories)),
Optional::get)));

Collectors類靜態工廠方法

在這裏插入圖片描述

7 收集器接口

Collector接口

public interface Collector<T, A, R> {
    Supplier<A> supplier();
    BiConsumer<A, T> accumulator();
    Function<A, R> finisher();
    BinaryOperator<A> combiner();
    Set<Characteristics> characteristics();
}

本列表適用以下定義。
 T是流中要收集的項目的泛型。
 A是累加器的類型,累加器是在收集過程中用於累積部分結果的對象。
 R是收集操作得到的對象(通常但並不一定是集合)的類型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章