You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Stream实现三数之和遇ConcurrentModificationException问题求助

问题描述

给定数组[6,4,7,9,3,12],需找出其中和为13的三个数,要求仅使用Java Stream实现,不能使用常规循环。尝试用map和flatMap实现时,因用到void方法不得不加入forEach循环,当前执行savedNumbers.add(fixMeCurrentNumber)时抛出ConcurrentModificationException,试过传递嵌套Wrapper对象无效,不想使用Iterator,求异常修复方案或更简洁的Stream实现思路。

原代码实现

DemoApplication.java

package com.example.demo_david;

import models.Wrapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);

        Integer value = 13;
        List<Integer> fixMe = new ArrayList<>(List.of(6,4,7,9,3,12));
        System.out.println("answer: " + findTheOnesThatEqualTheResult(value, fixMe));
    }

    static List<Integer> findTheOnesThatEqualTheResult(int maxValue, List<Integer> list){
        Wrapper wrapper = new Wrapper();

        List<ArrayList<Integer>> collectedList = list.stream().map(fixMeCurrentNumber ->
                computeLogic(maxValue, wrapper, fixMeCurrentNumber)).collect(Collectors.toList());

        List<Integer> flattenedList = collectedList.stream()
                .flatMap(Collection::stream)
                .collect(Collectors.toList());

        return flattenedList;
    }

    private static ArrayList<Integer> computeLogic(int maxValue, Wrapper wrapper, Integer fixMeCurrentNumber) {

        if((wrapper.getSavedNumbers() == null && fixMeCurrentNumber <= maxValue) ||
                (wrapper.getSavedNumbers().size() == 0 && fixMeCurrentNumber <= maxValue )){
            ArrayList<Integer> fixMeList = new ArrayList<>();
            fixMeList.add(fixMeCurrentNumber);
            wrapper.setSavedNumbers(fixMeList);
            wrapper.setCurrentTotalOfList(fixMeCurrentNumber);
        }
        else {
            ArrayList<Integer> copyToBeItereatedOver = new ArrayList<>();
            copyToBeItereatedOver = wrapper.getSavedNumbers();
            copyToBeItereatedOver.stream().forEach(savedNumber -> {
                 validateSavedNumbers(maxValue, wrapper, fixMeCurrentNumber);
            });
        }
        return wrapper.getSavedNumbers();
    }

    private static void validateSavedNumbers(int maxValue, Wrapper wrapper,
                                             Integer fixMeCurrentNumber) {
        if(wrapper.getCurrentTotalOfList() < maxValue &&
                wrapper.getCurrentTotalOfList() + fixMeCurrentNumber <= maxValue) {
            ArrayList<Integer> savedNumbers = new ArrayList<>();
            savedNumbers = wrapper.getSavedNumbers();
            //tried passing a wrapper and still no luck.
            savedNumbers.add(fixMeCurrentNumber);
            //wrapper.setSavedNumbers(savedNumbers);
            //wrapper.setCurrentTotalOfList(wrapper.getCurrentTotalOfList() + fixMeCurrentNumber);
        }
    }
}

Wrapper.java

package models;

import lombok.Data;

import java.util.ArrayList;


@Data
public class Wrapper {
    ArrayList<Integer> savedNumbers;
    int currentTotalOfList;
}

解决方案

1. ConcurrentModificationException 修复方案

异常核心原因:遍历copyToBeItereatedOver.stream()的同时直接修改了同一个ArrayList(wrapper.getSavedNumbers()),Stream的forEach遍历依赖迭代器,此时修改集合会触发快速失败机制。

修复思路:避免遍历原集合时修改它,改用创建新集合保存中间结果:

  • 在validateSavedNumbers中,创建原集合的副本添加元素后,再更新wrapper的引用;
  • 在computeLogic的else分支中,先获取原集合的快照,遍历快照的同时修改新集合。

修改后的关键代码片段:

private static void validateSavedNumbers(int maxValue, Wrapper wrapper, Integer fixMeCurrentNumber) {
    if(wrapper.getCurrentTotalOfList() < maxValue &&
            wrapper.getCurrentTotalOfList() + fixMeCurrentNumber <= maxValue) {
        // 创建原集合副本,避免遍历原集合时修改引发异常
        ArrayList<Integer> newSavedNumbers = new ArrayList<>(wrapper.getSavedNumbers());
        newSavedNumbers.add(fixMeCurrentNumber);
        wrapper.setSavedNumbers(newSavedNumbers);
        wrapper.setCurrentTotalOfList(wrapper.getCurrentTotalOfList() + fixMeCurrentNumber);
    }
}

同时调整computeLogic的else分支:

else {
    // 获取当前集合的快照,避免遍历过程中集合被修改导致异常
    ArrayList<Integer> snapshot = new ArrayList<>(wrapper.getSavedNumbers());
    snapshot.forEach(savedNumber -> {
         validateSavedNumbers(maxValue, wrapper, fixMeCurrentNumber);
    });
}

2. 更简洁的Stream实现思路

现有逻辑绕且易出错,推荐直接用Stream生成所有合法三元组,再过滤和为目标值的组合:

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class ThreeSumStream {
    public static void main(String[] args) {
        int target = 13;
        List<Integer> nums = List.of(6,4,7,9,3,12);
        
        List<List<Integer>> result = findThreeSum(nums, target);
        System.out.println("answer: " + result);
    }

    public static List<List<Integer>> findThreeSum(List<Integer> nums, int target) {
        return IntStream.range(0, nums.size())
                // 第一个数的索引i
                .flatMap(i -> IntStream.range(i + 1, nums.size())
                        // 第二个数的索引j,j > i避免重复组合
                        .flatMap(j -> IntStream.range(j + 1, nums.size())
                                // 第三个数的索引k,k > j
                                .filter(k -> nums.get(i) + nums.get(j) + nums.get(k) == target)
                                .mapToObj(k -> List.of(nums.get(i), nums.get(j), nums.get(k)))
                        )
                )
                .collect(Collectors.toList());
    }
}

该实现优势:

  • 完全基于Stream API,无常规循环;
  • 通过i < j < k的索引范围避免生成重复三元组;
  • 逻辑清晰,直接过滤符合条件的组合;
  • 无并发修改风险,因为未在遍历过程中修改集合。

若需扁平化结果(合并所有三元组元素为一个列表),可在最后添加.flatMap(List::stream).collect(Collectors.toList())。

内容的提问来源于stack exchange,提问作者David Westenberger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 18:52:03