java8--- groupingBy partitioningBy ,skip()+limit() 分頁


 

 

 //使用skip()和limit()進行分頁
    public static void pages() {
        int pageSize = 2; int pageIndex = 2; List<Integer> expected = Lists.newArrayList(61, 62, 63, 64, 65, 66, 67, 68, 69, 70); List<Integer> result = Stream.iterate(1, i -> i + 1) .skip((pageIndex - 1) * pageSize) .limit(pageSize) .collect(toList()); System.out.println(result); // assertEquals(expected, result); }

 

 

    //多級分組
    //----------------------------------
    //MEAT
    //    FAT
    //        [Test.Dish(name=pork, vegetarain=false, colories=800, type=MEAT)]
    //    DIET
    //        [Test.Dish(name=chicken, vegetarain=false, colories=400, type=MEAT)]
    //    NORMAL
    //        [Test.Dish(name=beef, vegetarain=false, colories=700, type=MEAT)]
    //----------------------------------
    //FISH
    //    DIET
    //        [Test.Dish(name=prawns, vegetarain=false, colories=300, type=FISH)]
    //    NORMAL
    //        [Test.Dish(name=salmon, vegetarain=false, colories=450, type=FISH)]
    //----------------------------------
    //OTHER
    //    DIET
    //        [Test.Dish(name=rice, vegetarain=true, colories=350, type=OTHER), Test.Dish(name=season fruit, vegetarain=true, colories=120, type=OTHER)]
    //    NORMAL
    //        [Test.Dish(name=french fries, vegetarain=true, colories=530, type=OTHER), Test.Dish(name=pizza, vegetarain=true, colories=550, type=OTHER)]
    //總結:groupingBy的核心參數為K生成器,V生成器。V生成器可以是任意類型的收集器Collector。
    public static void group2() {
        Map<Type, Map<CaloricLevel, List<Dish>>> byTypeAndCalory = dishes.stream().collect(
                groupingBy(Dish::getType,
                        groupingBy(d -> {
                                    /** 此處寫if的時候注意要顧及到所有的情況 */
                                    if (d.getColories() <= 400) {
                                        return CaloricLevel.DIET;
                                    } else if (d.getColories() <= 700) {
                                        return CaloricLevel.NORMAL;
                                    } else {
                                        return CaloricLevel.FAT;
                                    }
                                }
                        )
                )
        );
        byTypeAndCalory.forEach((type, byCalory) -> {
            System.out.println("----------------------------------");
            System.out.println(type);
            byCalory.forEach((level, dishList) -> {
                System.out.println("\t" + level);
                System.out.println("\t\t" + dishList);
            });
        });
    }

 

總結:groupingBy的核心參數為K生成器,V生成器。V生成器可以是任意類型的收集器Collector。

比如,V生成器可以是計算數目的, 從而實現了sql語句中的select count(*) from table A group by Type

Map<Type, Long> typesCount = dishes.stream().collect(groupingBy(Dish::getType, counting()));
System.out.println(typesCount);
-----------
{FISH=2, MEAT=3, OTHER=4}

 

sql查找分組最高分select MAX(id) from table A group by Type

Map<Type, Optional<Dish>> mostCaloricByType = dishes.stream()
        .collect(groupingBy(Dish::getType, maxBy(Comparator.comparingInt(Dish::getCalories))));

 

這里的Optional沒有意義,因為肯定不是null。那么只好取出來了。使用collectingAndThen

Map<Type, Dish> mostCaloricByType = dishes.stream()
    .collect(groupingBy(Dish::getType,
        collectingAndThen(maxBy(Comparator.comparingInt(Dish::getCalories)), Optional::get)));

 

到這里似乎結果出來了,但IDEA不同意,編譯黃色報警,按提示修改后變為:

Map<Type, Dish> mostCaloricByType = dishes.stream()
    .collect(toMap(Dish::getType, Function.identity(),
        BinaryOperator.maxBy(comparingInt(Dish::getCalories))));

是的,groupingBy就變成toMap了,key還是Type,value還是Dish,但多了一個參數!!這里回應開頭的坑,開頭的toMap演示是為了容易理解,真那么用則會被搞死。
我們知道把一個List重組為Map必然會面臨k相同的問題。當K相同時,v是覆蓋還是不管呢?前面的demo的做法是當k存在時,再次插入k則直接拋出異常: java.lang.IllegalStateException: Duplicate key Dish(name
=pork, vegetarian=false, calories=800, type=MEAT) at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133) 正確的做法是提供處理沖突的函數,在本demo中,處理沖突的原則就是找出最大的,正好符合我們分組求最大的要求。(真的不想搞Java8函數式學習了,感覺到處都是性能問題的坑)

 

繼續數據庫sql映射,分組求和select sum(score) from table a group by Type

Map<Type, Integer> totalCaloriesByType = dishes.stream()
    .collect(groupingBy(Dish::getType, summingInt(Dish::getCalories)));
然而常常和groupingBy聯合使用的另一個收集器是mapping方法生成的。這個方法接收兩個參數:一個函數對流中的元素做變換,另一個則將變換的結果對象收集起來。其目的是在累加之前對每個輸入元素應用一個映射函數,這樣就可以讓接收特定類型元素的收集器適應不同類型的對象。我么來看一個使用這個收集器的實際例子。比如你想得到,對於每種類型的Dish,菜單中都有哪些CaloricLevel。我們可以把groupingBy和mapping收集器結合起來,如下所示:

Map<Type, Set<CaloricLevel>> caloricLevelsByType = dishes.stream()
    .collect(groupingBy(Dish::getType, mapping(this::getCaloricLevel, toSet())));
這里的toSet默認采用的HashSet,也可以手動指定具體實現toCollection(HashSet::new)

 

https://yq.aliyun.com/articles/610729?spm=a2c4e.11153940.0.0.6a255562myIiAj

 

分區

 

看一個更加復雜也更為有趣的例子:將數組分為質數和非質數。

首先,定義個質數分區函數:


private boolean isPrime(int candidate) {
    int candidateRoot = (int) Math.sqrt((double) candidate);
    return IntStream.rangeClosed(2, candidateRoot).noneMatch(i -> candidate % i == 0);
}
然后找出1到100的質數和非質數

Map<Boolean, List<Integer>> partitionPrimes = IntStream.rangeClosed(2, 100).boxed()
    .collect(partitioningBy(this::isPrime));

 

 

例如,如果你是素食者,你可能想要把菜單按照素食和非素食分開:

Map<Boolean, List<Dish>> partitionedMenu = dishes.stream().collect(partitioningBy(Dish::isVegetarian));
當然,使用filter可以達到同樣的效果:

List<Dish> vegetarianDishes = dishes.stream().filter(Dish::isVegetarian).collect(Collectors.toList());
分區相對來說,優勢就是保存了兩個副本,當你想要對一個list分類時挺有用的。同時,和groupingBy一樣,partitioningBy一樣有重載方法,可以指定分組value的類型。

Map<Boolean, Map<Type, List<Dish>>> vegetarianDishesByType = dishes.stream()
    .collect(partitioningBy(Dish::isVegetarian, groupingBy(Dish::getType)));
Map<Boolean, Integer> vegetarianDishesTotalCalories = dishes.stream()
    .collect(partitioningBy(Dish::isVegetarian, summingInt(Dish::getCalories)));
Map<Boolean, Dish> mostCaloricPartitionedByVegetarian = dishes.stream()
    .collect(partitioningBy(Dish::isVegetarian,
        collectingAndThen(maxBy(comparingInt(Dish::getCalories)), Optional::get)));

 

  

基礎類

public enum CaloricLevel {
        DIET, NORMAL, FAT
    }
    public enum Type {
        MEAT, FISH, OTHER,
    }
    static List<Dish> dishes;
    static {
        dishes = Lists.newArrayList(
                new Dish("pork", false, 800, Type.MEAT),
                new Dish("beef", false, 700, Type.MEAT),
                new Dish("chicken", false, 400, Type.MEAT),
                new Dish("french fries", true, 530, Type.OTHER),
                new Dish("rice", true, 350, Type.OTHER),
                new Dish("season fruit", true, 120, Type.OTHER),
                new Dish("pizza", true, 550, Type.OTHER),
                new Dish("prawns", false, 300, Type.FISH),
                new Dish("salmon", false, 450, Type.FISH)
        );
    }
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @EqualsAndHashCode
    @ToString
    public static class Dish {
        private String name; ///** 名字 */
        private boolean vegetarain;///** 是否素食 */
        private int colories;///** 卡路里 */
        private Type type;///** 類型 */
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM