相关文章推荐
乐观的煎鸡蛋  ·  Dart语言基础Map、List、Set操作 ...·  2 月前    · 
坏坏的西瓜  ·  Flutter中Map、List数组的常用方 ...·  2 月前    · 
有胆有识的沙滩裤  ·  Dart语言基础Map、List、Set操作 ...·  2 月前    · 
性感的生菜  ·  C#计数List<string>中的连续重复 ...·  2 月前    · 
不羁的苦瓜  ·  Java怎么把多个对象的list的数据合并 ...·  1 月前    · 
帅气的面包  ·  网易新闻-头条版下载2024安卓最新版_手机 ...·  5 月前    · 
仗义的夕阳  ·  两年超额收益25%!“人神共奋”是怎么做到的 ...·  5 月前    · 
怕老婆的勺子  ·  南京大学招标办公室-地方政策法规·  11 月前    · 
重感情的红茶  ·  Git Diff中文乱码问题-阿里云开发者社区·  11 月前    · 
不羁的闹钟  ·  实现字符串转变量名的效果,VB实例教程之字符 ...·  1 年前    · 
Code  ›  Java8 Stream groupingBy对List进行分组开发者社区
test string list
https://cloud.tencent.com/developer/article/2120838
阳刚的小狗
5 月前
全栈程序员站长

Java8 Stream groupingBy对List进行分组

前往小程序,Get 更优 阅读体验!
立即前往
腾讯云
开发者社区
文档 建议反馈 控制台
首页
学习
活动
专区
工具
TVP
最新优惠活动
文章/答案/技术大牛
发布
首页
学习
活动
专区
工具
TVP 最新优惠活动
返回腾讯云官网
全栈程序员站长
首页
学习
活动
专区
工具
TVP 最新优惠活动
返回腾讯云官网
社区首页 > 专栏 > Java8 Stream groupingBy对List进行分组

Java8 Stream groupingBy对List进行分组

作者头像
全栈程序员站长
发布 于 2022-09-24 11:22:29
发布 于 2022-09-24 11:22:29
3.8K 0
举报
文章被收录于专栏: 全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

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

  • groupingBy

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

代码语言: javascript
复制
@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分组器,就可以这样操作:

代码语言: javascript
复制
/**
 * 使用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
代码语言: javascript
复制
/**
 * 使用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);
}
  • 统计分组平均值
代码语言: javascript
复制
/**
 * 使用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);
}
  • 统计分组总值
代码语言: javascript
复制
/**
 * 使用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
代码语言: javascript
复制
/**
 * 通过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
代码语言: javascript
复制
/**
 * 使用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
代码语言: javascript
复制
/**
 * 使用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
代码语言: javascript
复制
/**
 * 使用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
代码语言: javascript
复制
/**
 * 通过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源于工作的一个需求,存在如下数据结构:

代码语言: javascript
复制
@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操作。经过一番折腾,最终实现了功能,看一下示例代码:

代码语言: javascript
复制
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);
 
推荐文章
乐观的煎鸡蛋  ·  Dart语言基础Map、List、Set操作合辑开发者社区
2 月前
坏坏的西瓜  ·  Flutter中Map、List数组的常用方法_flutter map
2 月前
有胆有识的沙滩裤  ·  Dart语言基础Map、List、Set操作合辑__dart list.map
2 月前
性感的生菜  ·  C#计数List<string>中的连续重复开发者社区
2 月前
不羁的苦瓜  ·  Java怎么把多个对象的list的数据合并 - TechSynapse
1 月前
帅气的面包  ·  网易新闻-头条版下载2024安卓最新版_手机app官方版免费安装下载_豌豆荚
5 月前
仗义的夕阳  ·  两年超额收益25%!“人神共奋”是怎么做到的?|私募|公募基金_网易订阅
5 月前
怕老婆的勺子  ·  南京大学招标办公室-地方政策法规
11 月前
重感情的红茶  ·  Git Diff中文乱码问题-阿里云开发者社区
11 月前
不羁的闹钟  ·  实现字符串转变量名的效果,VB实例教程之字符串转换成变量名,好像全网都可能找不到更好的_vba字符串转换为变量名_老懒鸟呀的博客-CSDN博客
1 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号