ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

java8--- groupingBy

2020-01-14 17:57:34  阅读:470  来源: 互联网

标签:dishes groupingBy collect Dish stream Type java8


 

 

 

    //多级分组
    //----------------------------------
    //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还是Typevalue还是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。我们可以把groupingBymapping收集器结合起来,如下所示:

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);
}
然后找出1100的质数和非质数

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)));

 

 

 

 

 

 

 

 //使用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);
    }

 

 

 

 

基础类

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;///** 类型 */
    }

标签:dishes,groupingBy,collect,Dish,stream,Type,java8
来源: https://www.cnblogs.com/hahajava/p/12193317.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有