迭代处理后清除列表对象,解决CSV重复写入问题求助
问题分析与修复方案
嗨,我来帮你梳理下代码里的问题,以及怎么修复:
首先说核心问题:重复写入的根源
你现在所有文件内容相同,本质是匹配过的OutputCsvDataDto对象没有从outputDataList中移除,导致每个文件遍历的时候,都能匹配到全部对象,自然写入的内容一模一样。
另外你的嵌套循环逻辑完全绕反了:
- 遍历每个文件时,又从头遍历所有
addressLists和outputDataList,这会让同一个对象被反复匹配 - 还有
processFile里的对象赋值逻辑明显写错了,这会导致outputDataList里的对象数据根本不对
先修正processFile里的低级错误
你这段代码的赋值逻辑完全有问题:
this.csvOut = new OutputCsvDataDto(); this.csvOut=set.data ; // 这行是把csvOut的引用改成了set.data,不是给对象属性赋值! this.csvOut=set.value ; // 又把引用改成了set.value,之前新建的OutputCsvDataDto直接被丢弃了
正确的做法应该是给新建的对象的属性赋值,而且尽量用局部变量,避免成员变量带来的混乱:
public void processFile(Mapper mapper) { List<OutputCsvDataDto> outputDataList = new ArrayList<>(); Iterator<List<AddressDto>> iter = this.addressLists.iterator(); while (iter.hasNext()) { List<AddressDto> listOfAddress = iter.next(); for (AddressDto address : listOfAddress) { // 用局部变量,不要用成员变量this.csvOut,避免循环中引用混乱 OutputCsvDataDto csvOut = new OutputCsvDataDto(); // 这里改成你实际的属性赋值,比如: csvOut.setData(/* 对应的数据 */); csvOut.setValue(/* 对应的值 */); // 一定要把当前address关联到csvOut上,否则后续匹配不了 csvOut.setAddress(address); // 把对象加入列表 outputDataList.add(csvOut); } } // 等所有数据都准备好,再调用写入方法 processDataToResultantCsv(outputDataList); }
重构processDataToResultantCsv实现预期逻辑
我们要实现的是:每个文件写入一批匹配的对象,写完后把这些对象从列表中移除,后续文件不会再处理它们。这里必须用迭代器来安全移除元素,否则会抛出ConcurrentModificationException:
public void processDataToResultantCsv(List<OutputCsvDataDto> outputDataList) { Iterator<String> filesIterator = this.getOutgoingFileName().iterator(); while (filesIterator.hasNext()) { String fileName = filesIterator.next(); // 先收集当前文件要写入的所有数据 List<OutputCsvDataDto> dataForCurrentFile = new ArrayList<>(); // 遍历所有地址,匹配对应的输出对象 for (List<AddressDto> addressList : this.addressLists) { for (AddressDto address : addressList) { // 用迭代器遍历outputDataList,这样可以安全移除元素 Iterator<OutputCsvDataDto> outputIter = outputDataList.iterator(); while (outputIter.hasNext()) { OutputCsvDataDto output = outputIter.next(); if (address.equals(output.getAddress())) { dataForCurrentFile.add(output); outputIter.remove(); // 匹配到就立即从原列表移除,避免后续文件重复写入 break; // 每个address只匹配一个output,找到就跳出循环 } } } } // 只有当有数据的时候才写入文件 if (!dataForCurrentFile.isEmpty()) { this.writer.writeToCsv(dataForCurrentFile, fileName); } // 移除已经处理过的文件名 filesIterator.remove(); } }
几个关键的改进点
- 安全移除元素:用
Iterator.remove()来移除outputDataList中的元素,这是集合遍历中修改元素的标准方式,不会触发并发修改异常。 - 避免重复匹配:一旦某个
OutputCsvDataDto被匹配并加入当前文件的列表,就立即从原列表移除,后续文件遍历的时候就找不到它了。 - 批量写入:先收集当前文件需要的所有数据,再一次性写入,比循环多次调用
writeToCsv效率更高。 - 修复对象赋值:在
processFile中用局部变量创建OutputCsvDataDto,并正确给属性赋值,确保outputDataList中的数据是正确的。
内容的提问来源于stack exchange,提问作者user9193174
相关产品推荐
相关产品推荐

