首页 > 其他分享 >Collectors.toMap的暗坑与避免方式

Collectors.toMap的暗坑与避免方式

时间:2024-02-04 15:44:30浏览次数:34  
标签:map toMap Collectors list value User 暗坑

使用Java的stream中的Collectors可以很方便地做容器间的转换,可以少写很多代码。但是其中有暗含的坑需要注意和避免,本文探讨Collectors.toMap(JDK8版本)。

Collectors.toMap可以将一个流转化成Map,常见于需要将List转换成Map以便于进一步操作的场景,比如在通过RPC接口获取一个返回结果、从DB中查询到匹配的多条数据后,对其按某个字段(经常是主键id)做分组。这里先定义一个简单的类:

public class User {
    private Long id;

    private String name;
}

后续所有代码的目的均为将这个User类组成的List转换为id作为key、name作为value的Map。

1. 不使用stream

不使用stream时,需要先new一个map,然后手动把list的每一项放入map

    public void test0() {
        User user1 = new User();
        user1.setId(1L);
        user1.setName("1");

        User user2 = new User();
        user2.setId(2L);
        user2.setName("2");

        List<User> list = Lists.newArrayList();
        list.add(user1);
        list.add(user2);
        Map<Long, String> map = new HashMap<>();
        for(User user : list) {
            map.put(user.getId(), user.getName());
        }
    }

2. 使用stream的Collectors.toMap

构造list的代码不变,转化map的代码可以简化为

Map<Long, String> map = list.stream().collect(Collectors.toMap(User::getId, User::getName));

相较第一种,简洁多了。
但是,如果User1和User2的id是一样的,会发生什么情况?将代码中user2.setId(2L)修改为user2.setId(1L),再次执行,发现抛异常java.lang.IllegalStateException: Duplicate key 1,说明在merge时报错了:key不允许重复。

但是有些情况下key确实是可以重复的,比如我调用上游的数据,上游没做校验和控制;再或者这个字段本身不是惟一的,多个数据可能重复。那么如何改进呢?

3. Collectors.toMap指定merge函数

可以自定义一个merge函数来确定key重复时,如何取value。比如下面这种写法,是保留第一个value。你也可以保留第二个,或者是做一些更复杂的处理。

Map<Long, String> map = list.stream().collect(Collectors.toMap(User::getId, User::getName, (x1,x2)->x1));

4. value为null的场景

传入merge方法以后,看似万事大吉了,没想到还有坑。将user2.setName(null),会发现抛了NullPointerException异常,异常栈信息为:

可以看到对应的源码里,value是不允许为null的。

虽然说Map的value是支持null值的,但是map自己的merge方法天生不支持,此时仅靠自定义merge方法也已经无能为力了。如果仍然想使用Collectors.toMap,需要手动处理null的值,比如:

Map<Long, String> map = list.stream().collect(Collectors.toMap(User::getId, value -> Optional.ofNullable(value.getName()).orElse("")));

当然,这样处理后的map的value并不是实际的值,并不适用于所有场景。
这样看来,Collectors.toMap的局限性无法避免了,使用的时候要确认不会发生预期的问题。

当然你也可以做一些预处理,比如使用filter过滤掉value=null的数据,来规避这个问题。

5. key=null时会怎么样?

不会怎么样,一切正常。

public void test5() {
    User user1 = new User();
    user1.setId(null);
    user1.setName("1");

    User user2 = new User();
    user2.setId(2L);
    user2.setName("2");

    List<User> list = Lists.newArrayList();
    list.add(user1);
    list.add(user2);
    Map<Long, String> map = list.stream().collect(Collectors.toMap(User::getId, User::getName));
    System.out.print(map);
}

6. 还有什么要注意的?

上述的例子,均是建立在list不为空的前提下进行的。如果list本身为null,在调用stream()时自然也会抛NullPointerException;如果list是空的(内容为空但是容器本身初始化过,如list = new ArrayList<>()),则不会报错。

小结

  • 调用stream()前先确定容器本身是否为null
  • 如果不确定要通过Collectors.toMap转换为map的源容器的数据
    • 对应key是否会重复,可以在toMap()传入merge方法
    • 对应value是否为null,可以做过滤或指定默认值
  • 如果不想思考和求证,还是继续用for循环吧

标签:map,toMap,Collectors,list,value,User,暗坑
From: https://www.cnblogs.com/wuyuegb2312/p/18005914

相关文章

  • Stream toList不能滥用以及与collect(Collectors.toList())的区别
    StreamtoList()返回的是只读List原则上不可修改,collect(Collectors.toList())默认返回的是ArrayList,可以增删改查1.背景在公司看到开发环境突然发现了UnsupportedOperationException报错,想到了不是自己throw的应该就是操作collection不当。发现的确是同事使用了类似stringL......
  • toMap - bug
    原因:AttachmentInfoRespnse::getAssociateKeyId作为键重复了,代码没有指定如何处理//Duplicatekey:错误代码finalMap<Long,String>keyIdWithPathMap=attachmentInfoResponse.stream().collect(Collections.toMap(AttachmentInfoRespnse::getAssociateKeyId,item->Optional......
  • 使用AutoMapper
    1、在控制台中namespaceStudyAutoMapper{publicclassFoo{publicintID{get;set;}publicstringName{get;set;}}publicclassFooDto{publicintID{get;set;}publicstringName{get;se......
  • 在.net中使用AutoMapper进行对象映射,对象相互转,简单方便
    AutoMapper是一种对象映射工具,它可以帮助我们将不同类型的数据对象之间进行相互转换。在.NET中,我们可以使用AutoMapper库来简化数据对象之间的映射操作,从而提高代码的可读性和可维护性。一、AutoMapper的安装和基本使用安装AutoMapper首先,我们需要在项目中安装AutoMapper库。......
  • automapper 两种初始化方式
     另外一种 选择安装AutoMapper.Extensions.Microsoft.DependencyInjection这个包一种像下面这样收动配置,这种只需要引入基础包AutoMapper.IConfigurationProviderconfig=newMapperConfiguration(cfg=>{cfg.AddProfile<TradeApiMappingProfile>();cfg.AddProfil......
  • Util应用框架基础(二) - 对象到对象映射(AutoMapper)
    本节介绍Util应用框架相似对象之间的转换方法.文章分为多个小节,如果对设计原理不感兴趣,只需阅读基础用法部分即可.概述现代化分层架构,普遍采用了构造块DTO(数据传输对象).DTO是一种参数对象,当WebAPI接收到请求,请求参数被装载到DTO对象中.我们需要把DTO对象转换成实体......
  • .NET6 配置 AutoMapper 与 AutoFac
    AutoMapper 概述1、什么是AutoMapper简单来说,AutoMapper就是一个用C#语言开发的一个轻量的处理一个实体对象到另外一个实体对象映射关系的组件库 官网地址:AutoMapper 文档地址:AutoMapper—AutoMapperdocumentation 2、为什么要用AutoMapper层与层之间的数据转换......
  • .NET6 使用AutoMapper
    .NET6使用AutoMapper 一、Net6环境下的.netcore项目里如何使用AutoMapper实现依赖注入。注:AutoMapper是一个对象-对象映射器,可以将一个对象映射到另一个对象。第一步,在Nuget引入AutoMapper、AutoMapper.Extensions.DependencyInjection这两个NuGet包  ......
  • Java 通过collectors.groupingBy根据某个字段统计
    要使用Collectors.groupingBy根据某个字段统计,你可以通过提供一个函数来指定分组的条件。假设你有一个包含Person对象的列表,每个对象都有age字段表示年龄,你想要根据年龄分组,并统计每个年龄组的人数。以下是一个使用Collectors.groupingBy的示例代码:importjava.util.Arrays;imp......
  • Java 使用`Collectors.groupingBy`计算百分比
    要使用Collectors.groupingBy计算百分比,你需要先对数据进行分组,然后计算每个组内元素的百分比。假设你有一个包含整数的列表,你想要按照它们的奇偶性进行分组,并计算每个组内元素的百分比。以下是一个使用Collectors.groupingBy和自定义收集器的示例代码:importjava.util.Arrays;......