Java8 Stream groupingBy对List进行分组

Java8 Stream groupingBy对List进行分组提到GroupBy,首先想到的往往是sql中的groupby操作,对搜索结果进行分组。其实Java8StreamsAPI中的Collector也支持流中的数据进行分组和分区操作,本片文章讲简单介绍一下,如何使用groupingBy和partitioningBy来对流中的元素进行分组和分区。 groupingBy 首先看一下Java8之前如果想对一个List做分组操作,我们需要…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

提到Group By,首先想到的往往是sql中的group by操作,对搜索结果进行分组。其实Java8 Streams API中的Collector也支持流中的数据进行分组和分区操作,本片文章讲简单介绍一下,如何使用groupingBy 和 partitioningBy来对流中的元素进行分组和分区。

  • groupingBy

首先看一下Java8之前如果想对一个List做分组操作,我们需要如下代码操作:

@Test
public void groupListBeforeJava8() {
    Map<String, List<Employee>> result = new HashMap<>();
    for (Employee e : employees) {
        String city = e.getCity();
        List<Employee> empsInCity = result.get(city);
        if (empsInCity == null) {
            empsInCity = new ArrayList<>();
            result.put(city, empsInCity);
        }
        empsInCity.add(e);
    }
    System.out.println(result);
    assertEquals(result.get("London").size(), 2);
}

而如果使用Java8中Stream的groupingBy分组器,就可以这样操作:

/**
 * 使用java8 stream groupingBy操作,按城市分组list
 */
@Test
public void groupingByTest() {
    Map<String, List<Employee>> employeesByCity =
            employees.stream().collect(Collectors.groupingBy(Employee::getCity));
    System.out.println(employeesByCity);
    assertEquals(employeesByCity.get("London").size(), 2);
}

上面是groupingBy分组器最常见的一个用法,下面简单介绍一下其他用法:

  • 统计每个分组的count

/**
 * 使用java8 stream groupingBy操作,按城市分组list统计count
 */
@Test
public void groupingByCountTest() {
    Map<String, Long> employeesByCity =
            employees.stream().collect(Collectors.groupingBy(Employee::getCity, Collectors.counting()));
    System.out.println(employeesByCity);
    assertEquals(employeesByCity.get("London").longValue(), 2L);
}
  • 统计分组平均值

/**
 * 使用java8 stream groupingBy操作,按城市分组list并计算分组销售平均值
 */
@Test
public void groupingByAverageTest() {
    Map<String, Double> employeesByCity =
            employees.stream().collect(Collectors.groupingBy(Employee::getCity, Collectors.averagingInt(Employee::getSales)));
    System.out.println(employeesByCity);
    assertEquals(employeesByCity.get("London").intValue(), 175);
}
  • 统计分组总值

/**
 * 使用java8 stream groupingBy操作,按城市分组list并计算分组销售总值
 */
@Test
public void groupingBySumTest() {
    Map<String, Long> employeesByCity =
            employees.stream().collect(Collectors.groupingBy(Employee::getCity, Collectors.summingLong(Employee::getSales)));

    //对Map按照分组销售总值逆序排序
    Map<String, Long> finalMap = new LinkedHashMap<>();
    employeesByCity.entrySet().stream()
            .sorted(Map.Entry.<String, Long>comparingByValue()
                    .reversed()).forEachOrdered(e -> finalMap.put(e.getKey(), e.getValue()));

    System.out.println(finalMap);
    assertEquals(finalMap.get("London").longValue(), 350);
}
  • Join分组List

/**
 * 通过type分组list,通过join操作连接分组list
 */
@Test
public void groupingByConvertResultTest(){
    List<BlogPost> blogPostList = Lists.newArrayList();
    blogPostList.add(new BlogPost("post1", "zhuoli", 1, 30));
    blogPostList.add(new BlogPost("post2", "zhuoli", 1, 40));
    blogPostList.add(new BlogPost("post3", "zhuoli", 2, 15));
    blogPostList.add(new BlogPost("post4", "zhuoli", 3, 33));
    blogPostList.add(new BlogPost("post5", "Alice", 1, 99));
    blogPostList.add(new BlogPost("post6", "Michael", 3, 65));

    Map<Integer, String> postsPerType = blogPostList.stream()
            .collect(Collectors.groupingBy(BlogPost::getType,
                    Collectors.mapping(BlogPost::getTitle, Collectors.joining(", ", "Post titles: [", "]"))));
    System.out.println(postsPerType);
}
  • 转换分组结果List -> List

/**
 * 使用java8 stream groupingBy操作,按城市分组list,将List转化为name的List
 */
@Test
public void groupingByCityMapList(){
    Map<String, List<String>> namesByCity =
            employees.stream().collect(Collectors.groupingBy(Employee::getCity, Collectors.mapping(Employee::getName, Collectors.toList())));
    System.out.println(namesByCity);
    assertThat(namesByCity.get("London"), contains("Alice", "Bob"));
}
  • 转换分组结果List -> Set

/**
 * 使用java8 stream groupingBy操作,按城市分组list,将List转化为name的Set
 */
@Test
public void groupingByCityMapListToSet(){
    Map<String, Set<String>> namesByCity =
            employees.stream().collect(Collectors.groupingBy(Employee::getCity, Collectors.mapping(Employee::getName, Collectors.toSet())));
    System.out.println(namesByCity);
    assertThat(namesByCity.get("London"), containsInAnyOrder("Alice", "Bob"));
}
  • 使用对象分组List

/**
 * 使用java8 stream groupingBy操作,通过Object对象的成员分组List
 */
@Test
public void groupingByObjectTest(){
    List<BlogPost> blogPostList = Lists.newArrayList();
    blogPostList.add(new BlogPost("post1", "zhuoli", 1, 30));
    blogPostList.add(new BlogPost("post2", "zhuoli", 1, 40));
    blogPostList.add(new BlogPost("post3", "zhuoli", 2, 15));
    blogPostList.add(new BlogPost("post4", "zhuoli", 3, 33));
    blogPostList.add(new BlogPost("post5", "Alice", 1, 99));
    blogPostList.add(new BlogPost("post6", "Michael", 3, 65));

    Map<Tuple, List<BlogPost>> postsPerTypeAndAuthor = blogPostList.stream()
            .collect(Collectors.groupingBy(post -> new Tuple(post.getAuthor(), post.getType())));

    System.out.println(postsPerTypeAndAuthor);
}
  • 使用两个成员分组List

/**
 * 通过author和type分组list
 */
@Test
public void groupingByMultiItemTest(){
    List<BlogPost> blogPostList = Lists.newArrayList();
    blogPostList.add(new BlogPost("post1", "zhuoli", 1, 30));
    blogPostList.add(new BlogPost("post2", "zhuoli", 1, 40));
    blogPostList.add(new BlogPost("post3", "zhuoli", 2, 15));
    blogPostList.add(new BlogPost("post4", "zhuoli", 3, 33));
    blogPostList.add(new BlogPost("post5", "Alice", 1, 99));
    blogPostList.add(new BlogPost("post6", "Michael", 3, 65));

    Map<String, Map<Integer, List<BlogPost>>> map = blogPostList.stream()
            .collect(Collectors.groupingBy(BlogPost::getAuthor, Collectors.groupingBy(BlogPost::getType)));
    System.out.println(map);
}
  • 自定义DistinctBy对分组结果去重

使用groupingBy源于工作的一个需求,存在如下数据结构:

@Data
@AllArgsConstructor
public class TestData {
    private Integer scene;
    private Integer placement;
    private Long bid;
}

对TestData的List分组,统计每个sene已被占用的placement,我当时直接使用groupIngBy进行分组,得到了一个Map<Integer, List<Integer>的map,看似完成了目标需求,但当我审查结果的时候,发现List中存在重复现象。比如List<TestData>中存在多个Scene为1,placement也为1的元素,目标Map中key为1的value List中就会存在多个1,而实际上我们只需要一个1就能说明placement 1已经被占用了,所以我又希望通过distinct进行去重。但是Stream的distinct只能根据元素去重,并不能根据元素的某个成员去重,即distinctBy操作。经过一番折腾,最终实现了功能,看一下示例代码:

public class DistinctByKey {
    @Test
    public void distinctByKeyTest() {
        TestData testData1 = new TestData(1, 1, 100L);
        TestData testData2 = new TestData(1, 2, 1000L);
        TestData testData3 = new TestData(1, 3, 100L);
        TestData testData4 = new TestData(1, 1, 80L);

        TestData testData5 = new TestData(2, 1, 1600L);
        TestData testData6 = new TestData(2, 2, 1030L);
        TestData testData7 = new TestData(2, 2, 1001L);
        TestData testData8 = new TestData(2, 2, 1500L);

        TestData testData9 = new TestData(3, 5, 1500L);

        List<TestData> testDataList = Stream.of(testData1, testData2, testData3, testData4, testData5, testData6, testData7, testData8, testData9).collect(Collectors.toList());

        /*直接按照placement去重,scene为2的placement为1和2的元素被去掉*/
        List<TestData> distinctBykeyList = testDataList.stream().filter(distinctByKey(TestData::getPlacement)).collect(Collectors.toList());
        System.out.println(distinctBykeyList);

        Map<Integer, List<Integer>> resultMap = testDataList.stream().collect(Collectors.groupingBy(TestData::getScene)).entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey,
                        entry -> entry.getValue().stream().filter(distinctByKey(TestData::getPlacement)).map(TestData::getPlacement).collect(Collectors.toList())));
        System.out.println(resultMap);
    }

    private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return t -> seen.add(keyExtractor.apply(t));
    }
}

其实Stream的distinct方法,也是filter的一个特别实现。上述示例中distinctByKey也是通过filter,实现的,不多说,看一下示例代码就可以看明白。

示例代码:卓立 – 码云 – groupingBy操作

参考链接:

  1. Java 8 Streams API:对Stream分组和分区
  2. Java 8 – Stream Collectors groupingBy examples
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/171242.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • javaweb酒店管理系统源代码_java简单酒店管理系统代码

    javaweb酒店管理系统源代码_java简单酒店管理系统代码大家好,我是全微毕设团队的创始人,本团队擅长JAVA(SSM,SSH,SPRINGBOOT)、PYTHON、PHP、C#、安卓等多项技术。今天将为大家分析一个小型酒店管理系统(当前酒店管理系统已经得到了充足的发展和完善,一些大的酒店购买了功能强大、数据量处理快速、客户行为研究的软件,借此来提升酒店的核心竞争力。但是对于一些中小规模的酒店而言,昂贵的系统花费、专人的维护是不现实的,也是非常不灵活的。…

  • 身份验证错误 要求的函数不受支持 Windows远程桌面连接

    身份验证错误 要求的函数不受支持 Windows远程桌面连接PhpStorm绝对是PHP开发最好的开发工具。之前好好的能远程桌面连接到服务器,但是今天来就不能连接上了,并提示:身份验证错误。要求的函数不受支持。猜想可能是Windows又更新了什么鬼,后面查询资料知道是由于CredSSP加密Oracle修正的原因,这里我整理了下解决方案,希望能帮到你。微软给出解决方案:https://support.microsoft.com/zh-…

  • 震惊!2022 年秋招 Java 后端开发岗竟然一片红海!算法岗都不香了吗?

    据说,2022年算法岗遇冷,BAT暑期实习甚至收不到简历,Java反而爆炸。难道,Java的春天(映射Spring全家桶)又要来了吗?作为Java领域的优质创作者(见下图),又可以在CSDN横着走了吗?如何看待2022年秋招Java后端开发岗一片红海?这不仅让我想起脉脉上的一段话——来自某文豪。划重点:有后选后,无后选前无后无前,算法也甜条件允许,无脑后端前途无量,预定高管这里的后,Java后端敢称第二,没人敢称第一。为什么我敢这么肯定呢?从4个方面来说

  • Azure编程笔记(1):序列化复杂类型的TableEntity字段

    Azure编程笔记(1):序列化复杂类型的TableEntity字段

    2021年11月15日
  • 数字金额大小写转换

    数字金额大小写转换

    2021年12月17日
  • 使用ipset来批量控制iptables

    使用ipset来批量控制iptables配置如下1、安装ipsetyuminstallipset2、使用ipset创建列表ipsetcreateserverhash:ip3、添加ipipsetaddserver192.168.1.1ipsetaddserver192.168.1.24、导出ipsetipsetsave>/etc/sysconfig/ipset5、在导出到/etc/

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号