基于自定义类WiSeConSyncItem,移除子列表重复项后合并至主列表
这是个典型的多线程场景下的列表去重合并需求,我来给你拆解实现思路和具体代码方案:
核心需求分析
你需要解决两个关键问题:
- 高效判断子列表中的元素是否已存在于
mainItems(依据processId唯一标识) - 保证多线程操作
mainItems时的线程安全(毕竟mainItems同时被API循环任务和定时器修改)
具体实现方案
1. 基础去重方法(单线程场景)
如果你的场景暂时没有多线程并发修改的问题,基础版的去重逻辑就足够高效:
import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class SyncItemHandler { /** * 将子列表去重后合并到主列表 * @param mainItems 主列表 * @param subItems 需要合并的子列表 */ public void mergeUniqueItems(List<WiSeConSyncItem> mainItems, List<WiSeConSyncItem> subItems) { // 提取主列表中已有的processId集合,用于快速查重 Set<Integer> existingIds = mainItems.stream() .map(item -> item.processId) .collect(Collectors.toSet()); // 过滤子列表,只保留主列表中没有的元素 List<WiSeConSyncItem> uniqueSubItems = subItems.stream() .filter(item -> !existingIds.contains(item.processId)) .collect(Collectors.toList()); // 将去重后的元素添加到主列表 mainItems.addAll(uniqueSubItems); } }
2. 线程安全版本(多线程场景必用)
因为你提到mainItems同时被API循环(耗时操作)和定时器修改,而ArrayList本身不是线程安全的,直接操作会触发ConcurrentModificationException或者数据不一致问题,这里提供两种常用的线程安全方案:
方案A:同步块+普通ArrayList
用统一的锁对象包裹所有操作mainItems的代码,保证同一时间只有一个线程修改或读取列表:
public class SyncItemHandler { // 全局锁对象,所有操作mainItems的方法都要使用这个锁 private final Object listLock = new Object(); public void mergeUniqueItemsThreadSafe(List<WiSeConSyncItem> mainItems, List<WiSeConSyncItem> subItems) { synchronized (listLock) { Set<Integer> existingIds = mainItems.stream() .map(item -> item.processId) .collect(Collectors.toSet()); List<WiSeConSyncItem> uniqueSubItems = subItems.stream() .filter(item -> !existingIds.contains(item.processId)) .collect(Collectors.toList()); mainItems.addAll(uniqueSubItems); } } // 定时器添加元素的方法也要用同一个锁 public void addItemByTimer(List<WiSeConSyncItem> mainItems, WiSeConSyncItem newItem) { synchronized (listLock) { mainItems.add(newItem); } } }
方案B:使用CopyOnWriteArrayList线程安全集合
适合读多写少的场景,底层通过复制数组实现线程安全,读操作无锁:
import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; public class SyncItemHandler { // 初始化mainItems为线程安全的CopyOnWriteArrayList private List<WiSeConSyncItem> mainItems = new CopyOnWriteArrayList<>(); public void mergeUniqueItems(List<WiSeConSyncItem> subItems) { // CopyOnWriteArrayList的迭代是线程安全的,不会抛出并发修改异常 Set<Integer> existingIds = mainItems.stream() .map(item -> item.processId) .collect(Collectors.toSet()); List<WiSeConSyncItem> uniqueSubItems = subItems.stream() .filter(item -> !existingIds.contains(item.processId)) .collect(Collectors.toList()); // addAll操作自带线程安全保障 mainItems.addAll(uniqueSubItems); } }
性能优化建议
如果mainItems数据量很大,每次遍历提取processId会有开销,可以维护一个单独的线程安全集合来记录已存在的processId,避免重复遍历主列表:
import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; public class SyncItemHandler { private final Object lock = new Object(); private List<WiSeConSyncItem> mainItems = Collections.synchronizedList(new ArrayList<>()); private Set<Integer> existingProcessIds = Collections.synchronizedSet(new HashSet<>()); public void mergeUniqueItems(List<WiSeConSyncItem> subItems) { synchronized (lock) { List<WiSeConSyncItem> uniqueSubItems = subItems.stream() .filter(item -> !existingProcessIds.contains(item.processId)) .collect(Collectors.toList()); mainItems.addAll(uniqueSubItems); // 同步更新processId集合 uniqueSubItems.forEach(item -> existingProcessIds.add(item.processId)); } } public void addItem(WiSeConSyncItem item) { synchronized (lock) { mainItems.add(item); existingProcessIds.add(item.processId); } } }
内容的提问来源于stack exchange,提问作者noobEinstien
相关产品推荐
相关产品推荐

