Collectors.toMap踩坑指南:如何处理重复键与空指针问题?

vrep小车避障算法_V-REP 多车道巡线与避障 任务概要在给定的赛道中,实现多车道的避障和视觉巡线。避障算法没有限定,可使用人工势场、RRT等路径规划算法。车体大小、赛道信息以及参考资料在此处下载。完成情况已学习并能初步使用 V-REP Python Remote API 接口,实现 Python 与 V-REP 的功能交互。已实现基于视觉传感器的多车道巡线功能。已实现基于视觉传感器的多车道避障功能。V-REP Python Remote AP... 阅读详情

深入解析Collectors.toMap:从键冲突到空值处理的实战避坑指南

如果你在Java 8及以后的版本中处理过集合转换,大概率已经接触过Collectors.toMap()这个强大的工具。它让流(Stream)到Map的转换变得异常简洁,一行代码就能完成过去需要多行才能实现的功能。但正是这种简洁性,让不少开发者在实际项目中踩到了意想不到的坑——特别是当数据中存在重复键或者空值时,程序可能会在运行时抛出异常,让原本优雅的代码瞬间崩溃。

我在最近的一个数据迁移项目中就遇到了这样的问题。我们需要将用户列表转换为以用户ID为键的Map,原本以为简单的toMap()调用就能搞定,结果生产环境的数据中竟然存在重复的用户ID记录,直接导致了IllegalStateException。更棘手的是,部分用户的邮箱字段为null,这又引发了NullPointerException。这两个问题叠加,让我们的服务在凌晨的数据处理任务中意外中断。

这篇文章就是基于这些实战经验,深入探讨Collectors.toMap()的常见陷阱,并提供一套完整的解决方案。无论你是正在处理复杂数据转换的中高级Java工程师,还是希望深入理解Stream API的开发者,这些内容都将帮助你写出更健壮、更可靠的代码。

1. 理解Collectors.toMap的基本机制与潜在风险

在深入问题之前,我们先回顾一下Collectors.toMap()的基本用法。这个方法有多个重载版本,但最常用的是接受两个函数参数的形式:

Map<String, Integer> nameToAgeMap = people.stream()
    .collect(Collectors.toMap(
        Person::getName,      // 键提取函数
        Person::getAge        // 值提取函数
    ));

这段代码看起来简单明了,但背后隐藏着两个关键假设:

  1. 所有元素的键都是唯一的
  2. 所有元素的值都不为null

当这两个假设被打破时,问题就出现了。让我们通过一个简单的例子来看看会发生什么:

List<Person> people = Arrays.asList(
    new Person("Alice", 25),
    new Person("Bob", 30),
    new Person("Alice", 35)  // 重复的键
);

// 这会抛出IllegalStateException: Duplicate key Alice
Map<String, Integer> map = people.stream()
    .collect(Collectors.toMap(Person::getName, Person::getAge));

注意:IllegalStateException的错误信息通常是"Duplicate key [key name] (attempted merging values [value1] and [value2])"。这个异常是在收集过程的合并阶段抛出的,而不是在构建Map的初始阶段。

1.1 源码层面的问题根源

要真正理解这些问题,我们需要看看Collectors.toMap()的内部实现。在OpenJDK的源码中,toMap()方法最终会创建一个Map收集器,其核心逻辑包括:

  1. Supplier:创建一个新的HashMap(或其他指定的Map实现)
  2. Accumulator:将每个元素放入Map,键由keyMapper生成,值由valueMapper生成
  3. Combiner:在并行流中合并多个部分结果

问题的关键在于合并逻辑。当遇到重复键时,默认的实现会直接抛出异常,而不是提供任何冲突解决策略。这种设计选择有其合理性——在很多情况下,重复键确实表示数据有问题,开发者应该明确知道并处理这种情况。

1.2 实际业务场景中的常见问题

在我的经验中,toMap()的问题通常出现在以下几种场景:

场景类型具体表现潜在风险
数据库查询结果转换查询结果中存在逻辑上应该唯一但实际上重复的记录运行时异常,数据处理中断
外部API数据整合不同来源的数据可能有重复标识符数据丢失或服务不可用
缓存构建缓存键生成逻辑有缺陷导致冲突缓存失效,性能下降
配置加载配置文件中有重复的配置项应用启动失败

理解这些场景有助于我们在代码设计阶段就考虑潜在的风险,而不是等到问题发生后再去修复。

2. 处理重复键:从简单覆盖到复杂合并策略

当面对重复键问题时,我们有多种处理策略。Collectors.toMap()的第三个参数就是专门用来解决这个问题的——它是一个BinaryOperator,定义了当两个值对应同一个键时应该如何合并。

2.1 基本合并策略

最简单的策略是选择保留其中一个值。根据业务需求,你可以选择保留第一个值或最后一个值:

// 保留第一个出现的值
Map<String, Integer> keepFirst = people.stream()
    .collect(Collectors.toMap(
        Person::getName,
        Person::getAge,
        (existing, replacement) -> existing  // 总是返回已存在的值
    ));

// 保留最后一个出现的值(覆盖)
Map<String, Integer> keepLast = people.stream()
    .collect(Collectors.toMap(
        Person::getName,
        Person::getAge,
        (existing, replacement) -> replacement  // 总是用新值替换旧值
    ));

在实际项目中,我通常会更明确地表达意图,而不是使用简单的lambda表达式:

// 更清晰的意图表达
BinaryOperator<Integer> keepFirstStrategy = (oldValue, newValue) -> {
    log.warn("发现重复键,保留旧值: {} -> {}", oldValue, newValue);
    return oldValue;
};

BinaryOperator<Integer> keepLastStrategy = (oldValue, newValue) -> {
    log.warn("发现重复键,使用新值覆盖: {} -> {}", oldValue, newValue);
    return newValue;
};

2.2 高级合并策略

对于数值类型的值,我们可能需要更复杂的合并逻辑。比如求和、取平均值、找最大值/最小值等:

// 求和策略:适用于统计场景
BinaryOperator<Integer> sumStrategy = Integer::sum;

// 取最大值:适用于找最新或最优记录
BinaryOperator<Integer> maxStrategy = Math::max;

// 取最小值
BinaryOperator<Integer> minStrategy = Math::min;

// 自定义复杂合并:比如连接字符串
BinaryOperator<String> concatStrategy = (oldStr, newStr) -> 
    oldStr + ", " + newStr;

让我们看一个完整的例子,处理订单数据中同一用户的多次购买:

List<Order> orders = Arrays.asList(
    new Order("user1", 100.0),
    new Order("user2", 200.0),
    new Order("user1", 150.0),  // user1的第二次购买
    new Order("user3", 300.0)
);

// 计算每个用户的总消费金额
Map<String, Double> userTotalSpent = orders.stream()
    .collect(Collectors.toMap(
        Order::getUserId,
        Order::getAmount,
        Double::sum,  // 金额相加
        TreeMap::new  // 使用TreeMap按用户ID排序
    ));

System.out.println(userTotalSpent);
// 输出: {user1=250.0, user2=200.0, user3=300.0}

2.3 处理复杂对象的合并

当值不是简单类型而是复杂对象时,合并策略需要更多考虑。假设我们有一个UserStats类,包含用户的多种统计信息:

class UserStats {
    private long visitCount;
    private double totalTimeSpent;
    private LocalDateTime lastVisit;
    
    // 合并两个统计对象
    public UserStats merge(UserStats other) {
        return new UserStats(
            this.visitCount + other.visitCount,
            this.totalTimeSpent + other.totalTimeSpent,
            this.lastVisit.isAfter(other.lastVisit) ? 
                this.lastVisit : other.lastVisit
        );
    }
}

// 使用自定义合并方法
Map<String, UserStats> userStatsMap = logs.stream()
    .collect(Collectors.toMap(
        Log::getUserId,
        log -> new UserStats(1, log.getDuration(), log.getTimestamp()),
        UserStats::merge
    ));

3. 空值处理:预防NullPointerException的多种方案

空指针异常是Collectors.toMap()的另一个常见陷阱。当keyMapper或valueMapper返回null时,默认实现会抛出NullPointerException。这个问题在JDK 8和JDK 9+中的行为有所不同,需要特别注意。

3.1 JDK版本差异带来的行为变化

在JDK 8中,HashMap的merge方法不允许null值,因此当valueMapper返回null时,toMap()会抛出NullPointerException。但在JDK 9及更高版本中,这个限制被放宽了,HashMap可以接受null值。

然而,依赖JDK版本的行为差异并不是好主意。更好的做法是明确处理空值,确保代码在所有JDK版本上行为一致。

3.2 过滤空值策略

最直接的处理方式是在流中过滤掉可能产生空值的元素:

// 过滤掉键为null的元素
Map<String, Integer> map1 = people.stream()
    .filter(p -> p.getName() != null)
    .collect(Collectors.toMap(
        Person::getName,
        Person::getAge
    ));

// 过滤掉值为null的元素
Map<String, String> map2 = people.stream()
    .filter(p -> p.getEmail() != null)
    .collect(Collectors.toMap(
        Person::getName,
        Person::getEmail
    ));

这种方法简单有效,但可能会丢失数据。如果业务上不能接受数据丢失,就需要其他方案。

3.3 提供默认值策略

当值为null时,提供一个合理的默认值:

// 为null的年龄提供默认值-1
Map<String, Integer> mapWithDefault = people.stream()
    .collect(Collectors.toMap(
        Person::getName,
        p -> p.getAge() != null ? p.getAge() : -1
    ));

对于更复杂的情况,可以使用Optional来优雅地处理:

Map<String, String> safeMap = people.stream()
    .collect(Collectors.toMap(
        Person::getName,
        p -> Optional.ofNullable(p.getEmail())
                    .orElse("no-email@example.com")
    ));

3.4 自定义收集器处理空值

如果需要在多个地方复用空值处理逻辑,可以创建自定义的收集器:

public static <T, K, U> Collector<T, ?, Map<K, U>> toMapWithNullSafety(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends U> valueMapper,
        BinaryOperator<U> mergeFunction) {
    
    return Collectors.toMap(
        keyMapper,
        valueMapper,
        (oldVal, newVal) -> {
            if (oldVal == null) return newVal;
            if (newVal == null) return oldVal;
            return mergeFunction.apply(oldVal, newVal);
        }
    );
}

// 使用自定义收集器
Map<String, Integer> safeMap = people.stream()
    .collect(toMapWithNullSafety(
        Person::getName,
        Person::getAge,
        Integer::sum
    ));

4. 高级技巧与最佳实践

掌握了处理重复键和空值的基本方法后,让我们看看一些更高级的技巧和最佳实践,这些内容来自我多年处理复杂数据转换的经验。

4.1 选择合适的Map实现

Collectors.toMap()的第四个参数允许我们指定Map的实现类。根据不同的需求,我们可以选择不同的Map实现:

// 需要按键排序时使用TreeMap
Map<String, Integer> sortedMap = people.stream()
    .collect(Collectors.toMap(
        Person::getName,
        Person::getAge,
        (v1, v2) -> v1,  // 保留第一个值
        TreeMap::new
    ));

// 需要保持插入顺序时使用LinkedHashMap
Map<String, Integer> orderedMap = people.stream()
    .collect(Collectors.toMap(
        Person::getName,
        Person::getAge,
        (v1, v2) -> v1,
        LinkedHashMap::new
    ));

// 需要并发安全时(注意:toConcurrentMap可能是更好的选择)
Map<String, Integer> concurrentMap = people.stream()
    .collect(Collectors.toMap(
        Person::getName,
        Person::getAge,
        (v1, v2) -> v1,
        ConcurrentHashMap::new
    ));

4.2 性能考虑与调优建议

在处理大数据集时,toMap()的性能可能成为瓶颈。以下是一些优化建议:

  1. 预估Map大小:如果知道大致的数据量,可以在创建Map时指定初始容量
  2. 避免不必要的装箱:对于原始类型,考虑使用专门的收集器
  3. 并行流谨慎使用:并行流中的toMap需要合并操作,可能抵消并行化的好处
// 自定义Map供应商以指定初始容量
Supplier<Map<String, Integer>> mapSupplier = () -> new HashMap<>(1000);

Map<String, Integer> optimizedMap = largeCollection.stream()
    .collect(Collectors.toMap(
        Person::getName,
        Person::getAge,
        (v1, v2) -> v1,
        mapSupplier
    ));

4.3 调试与日志记录

当toMap()出现问题时,调试可能比较困难,因为异常信息可能不够详细。我通常会在合并函数中添加日志记录:

BinaryOperator<Integer> loggingMerger = (oldVal, newVal) -> {
    if (!oldVal.equals(newVal)) {
        log.debug("合并重复键的值: {} -> {}", oldVal, newVal);
        // 根据业务逻辑决定返回哪个值
        return Math.max(oldVal, newVal);
    }
    return oldVal;
};

Map<String, Integer> map = items.stream()
    .collect(Collectors.toMap(
        Item::getKey,
        Item::getValue,
        loggingMerger
    ));

4.4 与groupingBy的对比选择

有时候,Collectors.groupingBy()可能是比toMap()更好的选择,特别是当你需要对值进行复杂聚合时:

// 使用toMap:需要显式处理重复键
Map<String, List<Order>> ordersByUser1 = orders.stream()
    .collect(Collectors.toMap(
        Order::getUserId,
        Collections::singletonList,
        (list1, list2) -> {
            List<Order> merged = new ArrayList<>(list1);
            merged.addAll(list2);
            return merged;
        }
    ));

// 使用groupingBy:更简洁
Map<String, List<Order>> ordersByUser2 = orders.stream()
    .collect(Collectors.groupingBy(Order::getUserId));

选择哪个方法取决于具体需求:

  • 使用toMap当:需要完全控制键值映射,特别是值不是从元素直接派生时
  • 使用groupingBy当:需要按某个属性分组并对组内元素进行聚合操作时

5. 实战案例:从真实业务场景中学习

理论说了这么多,让我们通过几个真实的业务场景来看看如何应用这些技巧。

5.1 场景一:用户数据去重与合并

假设我们从多个数据源获取用户信息,需要合并成一个用户Map,处理重复用户和缺失字段:

List<User> usersFromSourceA = fetchUsersFromSourceA();
List<User> usersFromSourceB = fetchUsersFromSourceB();

// 合并两个数据源,处理重复和空值
Map<String, User> mergedUsers = Stream.concat(
        usersFromSourceA.stream(),
        usersFromSourceB.stream()
    )
    .filter(user -> user.getId() != null)  // 过滤掉ID为null的用户
    .collect(Collectors.toMap(
        User::getId,
        Function.identity(),
        (userA, userB) -> {
            // 合并策略:优先使用非空字段,时间戳最新的优先
            User merged = new User();
            merged.setId(userA.getId());
            merged.setName(userA.getName() != null ? userA.getName() : userB.getName());
            merged.setEmail(userA.getEmail() != null ? userA.getEmail() : userB.getEmail());
            merged.setPhone(userA.getPhone() != null ? userA.getPhone() : userB.getPhone());
            
            // 选择更新时间更近的记录
            if (userA.getUpdatedAt() != null && userB.getUpdatedAt() != null) {
                merged.setUpdatedAt(userA.getUpdatedAt().isAfter(userB.getUpdatedAt()) ? 
                                  userA.getUpdatedAt() : userB.getUpdatedAt());
            } else if (userA.getUpdatedAt() != null) {
                merged.setUpdatedAt(userA.getUpdatedAt());
            } else {
                merged.setUpdatedAt(userB.getUpdatedAt());
            }
            
            return merged;
        },
        LinkedHashMap::new  // 保持处理顺序
    ));

5.2 场景二:构建缓存时的异常处理

在构建本地缓存时,我们需要确保即使部分数据有问题,缓存也能正常构建:

public Map<String, Product> buildProductCache(List<Product> products) {
    try {
        return products.stream()
            .filter(product -> product != null && product.getSku() != null)
            .collect(Collectors.toMap(
                Product::getSku,
                Function.identity(),
                (existing, replacement) -> {
                    log.warn("发现重复SKU: {},使用更新版本", existing.getSku());
                    return existing.getVersion() > replacement.getVersion() ? 
                           existing : replacement;
                },
                () -> {
                    // 使用WeakHashMap避免内存泄漏
                    Map<String, Product> cache = new WeakHashMap<>(products.size());
                    log.info("初始化产品缓存,预计大小: {}", products.size());
                    return cache;
                }
            ));
    } catch (Exception e) {
        log.error("构建产品缓存失败,返回空缓存", e);
        return Collections.emptyMap();
    }
}

5.3 场景三:数据转换与验证流水线

在数据管道中,我们经常需要将数据从一种格式转换为另一种格式,同时进行验证和清理:

public Map<String, CleanData> processRawData(List<RawData> rawDataList) {
    return rawDataList.stream()
        .filter(this::isValidRawData)  // 第一步:过滤无效数据
        .map(this::transformRawData)    // 第二步:转换数据格式
        .filter(Objects::nonNull)       // 第三步:过滤转换失败的数据
        .collect(Collectors.toMap(
            CleanData::getKey,
            Function.identity(),
            this::handleDuplicateCleanData,  // 第四步:处理重复数据
            TreeMap::new                     // 第五步:排序结果
        ));
}

private CleanData handleDuplicateCleanData(CleanData existing, CleanData newData) {
    // 复杂的重复数据处理逻辑
    if (existing.getQualityScore() >= newData.getQualityScore()) {
        log.debug("保留质量更高的数据: {}", existing.getKey());
        return existing;
    } else {
        log.debug("用质量更高的数据替换: {}", newData.getKey());
        return newData;
    }
}

这些实战案例展示了如何将前面讨论的技术组合起来解决实际问题。关键是要根据具体的业务需求选择合适的策略,而不是生搬硬套某种模式。

在长期使用Collectors.toMap()的过程中,我发现最有效的做法是在项目早期就建立统一的数据处理规范。比如,在团队内部约定所有使用toMap()的地方都必须提供合并函数,即使当前数据看起来没有重复键;对于可能为null的值,要明确处理策略并在文档中说明。这种预防性的做法虽然需要一些额外的工作,但能避免很多潜在的生产问题。

另一个实用的经验是创建项目专用的工具类,封装常见的toMap()使用模式。这样不仅提高了代码复用性,还能确保整个项目在处理相同问题时采用一致的方法。比如,你可以创建一个CollectionUtils类,提供safeToMap()、toMapWithDefault()等方法,让团队成员可以轻松地使用经过验证的最佳实践。

PaddleOCR在医疗票据识别中的实战:从部署到业务字段提取 本文详细介绍了PaddleOCR在医疗票据识别中的实战应用,重点阐述了其离线部署方案与PP-OCRv3模型选型优势。针对医疗票据敏感数据不出本地的合规要求,文章提供了从环境搭建、模型配置到核心字段提取策略的完整流程,并分享了性能优化与系统集成的实践经验,有效解决了医疗票据自动化录入的难题。 阅读详情

相关推荐

Prometheus安装部署说明

1. 安装Prometheus Server   Prometheus基于Golang编写,编译后的软件包,不依赖于任何的第三方依赖。用户只需要下载对应平台的二进制包,解压并且添加基本的配置即可正常启Prometheus Server。 1.1 下载并解压二进制安装包 通过Prometheus的官方网站:https://prometheus.io/download/,下载最新版本的Prometheus安装包 #下载、解压、创建软链接 cd /export/ wget https://githu..

m0_38100619的博客 5014

Java8 Collectors.toMap() 的使用

Collectors.toMap(): JDK8 中提供,用于将 Stream 流转换为 Map。 用法1:根据某一属性,对对象的实例或属性做映射 例如:使用 Stream 想要将集合的某一属性(例如手机号)作为 key,对象本身作为 value,这样我们在根据属性获取实例或实例的其他属性时就可以省去遍历每个对象的时间。

ACGkaka的博客 4万+

手把手教你逆向抖店滑块验证码:从抓包到生成captchaBody的完整流程(附Python复现代码)

本文详细解析了抖店滑块验证码的逆向过程,从抓包分析到参数生成,最终用Python完整复现验证流程。涵盖关键接口分析、指纹参数生成、轨迹加密逻辑及核心加密算法实现,帮助开发者突破抖店的多重安全机制,实现高效的人机验证绕过。

weixin_30681121的博客 427

Collectors.toMap 根据字段去重

Collectors.toMap

A 1931

Ubuntu24.04下Librenms的完整安装与配置指南

本文提供了在Ubuntu 24.04 LTS系统上完整安装与配置LibreNMS网络监控系统的详细指南。内容涵盖系统环境准备、依赖安装、源码获取、PHP/数据库/Nginx配置、Composer安装、网页向导设置以及设备添加与日常使用,帮助用户快速部署一套功能强大的开源网络监控解决方案。

tgb3456789的博客 1140

在Visual Studio中搭建Dynamo Python开发环境,效率飞一般的增长

在Visual Studio中搭建Dynamo Python开发环境,效率飞一般的增长

BIM的乐趣 1731

List集合之元素和对象去重

文章目录1 使用的实体类2 利用Collectors.toMap去重2.1 toMap去重说明2.2 Funcion.identity()解释3 利用Collectors.toCollection和TreeSet去重 1 使用的实体类 @Data @NoArgsConstructor @AllArgsConstructor @String public class User(){ private String userId; private String userName; private Integ

上善若泪 1万+

(3万字深度解析)Kaggle《房价预测:高级回归技巧》详解复现攻略

在这篇教程式文章中,我们从零开始完成了 Kaggle《房价预测:高级回归技巧》比赛的复现工作,涵盖了数据预处理、特征工程、模型训练、调优、解释和融合的完整流程。深入理解数据:阅读数据描述,结合常识对每个特征作出合理处理(如缺失值填充’None’表示无此设施 、异常值剔除 等)。对目标和偏态特征进行对数变换,使分布更平滑,从而更好满足模型假设并提升准确度。特征工程:我们创造了总面积、总浴室等新特征,整合信息提高模型可学习性。

qq_38334677的博客 1776

Collectors.toMap出现重复key问题

使用合适的 mergeFunction:我们可以在调用 toMap 方法时指定一个合适的 mergeFunction 来处理 key 相同的情况。例如,我们可以选择保留旧值或新值等。toMap 使用 重复 key 问题 可以用 (k1,k2)->k1 来设置,如果有重复的 key,则保留 key1,舍弃 key2。

IT枫斗者的博客 5876

PCIE配置和地址空间

来源:https://www.jianshu.com/p/574e9a2cbc4e 每个PCIe设备,有这么一段空间,Host软件可以读取它获得该设备的一些信息,也可以通过它来配置该设备,这段空间就叫做PCIe的配置空间。不同于每个设备的其它空间,PCIe设备的配置空间是协议规定好的,哪个地方放什么内容,都是有定义的。PCI或者PCI-X时代就有配置空间的概念,那时的配置空间如下: 图6.1 整个配置空间就是一系列寄存器的集合,其中Type 0是Endpoint的配置,Type 1是Brid.

qq_28643619的博客 4063

【硬刚Hadoop】HADOOP MAPREDUCE(1):概述

本文是对《【硬刚大数据之学习路线篇】从零到大数据专家的学习指南(全面升级版)》的Hadoop部分补充。 1 MapReduce定义 MapReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。 MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上。 2MapReduce优缺点 2.1 优点 1)MapReduce易于编程 它简单的实现一些接口,就可以完成一个分布式程序..

微信搜:import_bigdata,大数据领域硬核原创作者 464

org.apache.rocketmq.client.exception.MQClientException: The producer service state not OK, maybe sta

org.apache.rocketmq.client.exception.MQClientException: The producer service state not OK, maybe started once, RUNNING See http://rocketmq.apache.org/docs/faq/ for further details. at org.apache.rock...

梦之马 8840

【AI理论学习】手把手推导扩散模型:Diffusion Models(DDPM)

生成扩散模型DDPM如下图所示分为前向、逆向两个过程,它首先通过不断往原始清晰数据中添加噪声使其变成标准高斯噪声(前向过程),而后期望从标准高斯噪声中还原原始数据(逆向过程)。若能实现,那便可从已知的标准高斯分布中采样一个噪声数据,而后利用DDPM模型生成符合原始数据分布的新数据。 Diffusion模型包含两个过程:数学符号:y∝xy\propto xy∝x:y正比于x,即y随着x增大而线性增大。 条件概率:贝叶斯公式:p(A∣B)=p(B∣A)×p(A)p(B)p(A|B)=\frac{p(B|A)\

小哲的博客 1512

Collectors.toMap使用详解

如果映射的键可能有重复项,请改用 toMap(Function, Function, BinaryOperator)。toMap(Function, Function) 返回一个 Collector,它将元素累积到一个 Map中,其键和值是将提供的映射函数应用于输入元素的结果。list.add(new Student(i+"","学生"+i));list1.add(new Student(i+"","学生"+i));list2.add(new Student(i+"","学生"+i));...

4万+

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Collectors.toMap报错:空指针 & key重复

Java 8中的stream在项目开发中被同学们用的风生水起,当然大家也踩了不少坑。下面我就来说说Collections.toMap在项目使用中踩的坑,避免大家重复被坑。

weixin_43805705的博客 5893

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Java8之Stream流的Collectors.toMap避坑指南

玩转Stream流之toMap避坑指南

weixin_49190101的博客 7787

Java8 Collectors.toMap的两个大坑

文章目录一. 坑1:Duplicate Key时抛出IllegalStateException异常1. 概述2. 大坑复现3. 大坑解决二. 坑2:value为空时抛出NullPointerException异常1. 概述2. 大坑复现3. 大坑解决3.1 法1:value值判空设置3.2 法2:使用`collect(Supplier supplier, BiConsumer Collector...

zijikanwa 7425

JDK8-Collectors.toMap异常处理

文章目录Collectors.toMap异常键冲突 ?空指针异常 ? Collectors.toMap异常 在使用Collectors.toMap方法时,会有两个常见的问题:1. 键冲突,导致抛出异常 2. 值为NULL,导致抛出空指针异常 下面给出上面两种问题的解决方案。 键冲突 ? 有三种策略,分别如下: 用后面的value覆盖前面的value Map&amp;lt;String, String&amp;...

stone_yw的博客 6449

Java小白也能懂的Collectors.toMap指南

对于更复杂的例子,比如要把整个学生对象作为值,或者需要多层嵌套的Map结构,平台也能很好地支持。作为一个刚接触Java集合操作的新手,我发现这个方法在处理数据转换时特别方便,尤其是需要把列表转换成键值对映射的场景。想象一下,你有一份班级学生名单(List),现在需要把它转换成通讯录(Map),其中学号作为键,学生姓名作为值。[Student(101,"张三"), Student(102,"李四"), Student(103,"王五")]如果遇到学号重复的情况,可以选择保留先出现的记录。102: "李四",

OnyxPanther23的博客 903

使用stream的Collectors.toMap()方法常见问题

使用stream的Collectors.toMap()方法常见问题

weixin_49114503的博客 4480

JavaStream流Collectors.toMap()踩坑

使用Collectors.toMap()时常见DuplicateKey和NullPointerException问题。原因在于底层Map.merge()方法限制:merge()不允许null值,且默认会抛出DuplicateKey异常。解决方案:1)处理重复key时指定保留策略(如保留第一个或最后一个);2)处理null值时设置默认值替代。这些方法能有效规避异常,确保流式操作顺利进行。

weixin_45895301的博客 584

java8的Collector.toMap值为空的异常

最近在使用lambda表达式的Collectors.toMap方法时就遇到了一个问题。 大致源码如下: public class Test { public static void main(String[] args) { // initMemberList为获取数据的方法 List<Member> list = Test.initMemberList(); Map<String, String> memberMap = list.stream().collect(Co

LiZhen314的博客 3839

java8 Stream流Collectors.toMap value为空报错

public class Test { public static void main(String[] args) { List<User> list = new ArrayList<>(); User user = new User(); user.setName("jack"); list.add(user); // 报错 list.stream().collect(Collector

qq_31086797的博客 1万+

!!!警惕 Collectors.toMap() 中的 Null 值坑!全网最细讲解+示例

场景是否会报错解决方式value 为 null✅ 会报 NPEkey 重复添加 mergeFunction 解决冲突普通 map.put(k, null)❌ 不会报错✅ 安全Stream toMap 遇 null✅ 报错加默认值 or 过滤 null是 Java 8 Stream 中非常强大的工具,但它对 null 不太宽容,稍有不慎就可能在线上环境踩雷。希望这篇博客能让你彻底掌握它的使用细节,写出更稳健的 Java 代码!📌 有其他 Stream 踩坑想法?评论区见!

m0_64934710的博客 858

Collections.toMap 报错 空指针 & key重复

Collections.toMap作用 将list按照规则转成map。 books.stream().collect(Collectors.toMap(Book::getIsbn, Book::getName)); 存在的各种坑 空指针 key值重复 空指针 & key重复 重复key 后边会覆盖前边的值 描述问题:当value为null的时候会抛出异常(Book::ge...

巡山小妖008 694

stream流—关于Collectors.toMap使用详解

stream流的使用,分组groupingby以及Collectors.toMap介绍

大龄烤红薯的博客 9028

java8Collectors.toMap 的value为null的时候报NullPointException ,key重复报IllegalStateException: Duplicate key

使用jdk8对于熟悉的的还好,要是不熟悉,肯定会有很多坑 有时候我们查询数据库得到一个List集合,然后将list集合使用Stream流将List集合映射成map,如下面的形式: ArrayList<Person> persons= new ArrayList<>(); Person person1 = new Person(); ...

tom有cat 5829
上一篇: DM数据库迁移实战:如何用dimp和dexp搞定跨版本数据导入导出(附常见错误解决方案)
下一篇: Visual Studio 2022环境下vcpkg安装zlib失败的终极解决方案(附详细日志分析)
docker8compose
博客等级 码龄1天 0粉丝 1102原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值