使用putIfAbsent()操作流时遇UnsupportedOperationException问题排查
问题排查与解决方案
问题根源
抛出UnsupportedOperationException的核心原因及其他问题:
- 不可修改Map:
x.getTaskResults()返回的是不可修改的Map(比如通过Map.of()、Collections.unmodifiableMap()创建),调用putIfAbsent这类修改操作会直接触发异常。 - 并行流线程不安全:用
parallel()并行操作同一个Map,即使Map可修改,也会引发并发修改问题或数据不一致。 - 条件逻辑错误:原代码
allHistoryTasks.containsAll(x.getTaskResults().keySet())是判断x的任务结果包含所有历史任务,和需求“包含任意一个历史任务则补全”不符。
修复方案
- 创建新的可修改Map,避免修改原不可变Map;
- 移除并行流,改为普通循环或串行流,保证线程安全;
- 修正条件判断,检查x的任务结果是否包含任意一个历史任务。
修复后代码
private Set<String> allHistoryTasks = Set.of("Phalanxing", "Shieldwalling", "Tercioing", "Wedging"); public Map<Person, Map<String, Integer>> addHistoryIfPresent(Stream<CourseResult> stream) { return stream.collect(Collectors.toMap( CourseResult::getPerson, x -> { // 创建新的可修改HashMap,复制原任务结果 Map<String, Integer> taskResults = new HashMap<>(x.getTaskResults()); // 检查是否包含任意一个历史任务 boolean hasHistoryTask = taskResults.keySet().stream() .anyMatch(allHistoryTasks::contains); if (hasHistoryTask) { // 补全所有历史任务,不存在则设为0 allHistoryTasks.forEach(task -> taskResults.putIfAbsent(task, 0)); } return taskResults; } )); }
额外说明
- 用
HashMap包装原Map,既保留原有数据,又支持修改操作; - 用
anyMatch替代原错误的containsAll,符合“包含特定任务则补全”的需求; - 直接遍历
allHistoryTasks集合,比转数组再用IntStream更简洁直观。
内容的提问来源于stack exchange,提问作者Oleh
相关产品推荐
相关产品推荐

