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

如何在CopyOnWriteArrayList中安全添加不重复元素(保留插入顺序)?

线程安全的有序无重复列表实现优化方案

问题描述

我需要保证代码线程安全,因此使用了CopyOnWriteArrayList:

List<String> fruits = new CopyOnWriteArrayList<>();

我希望列表中不存在重复元素,同时还要保留插入顺序,因此无法使用Set。我编写了如下代码:

public boolean add(String fruit) {
    synchronized (fruits) {
        if (fruits.contains(fruit)) {
            return false;
        }
        fruits.add(fruit);
    }
    return true;
}

由于我知道CopyOnWriteArrayList本身已是并发安全的,因此将其add方法放在synchronized块中似乎并非良策,想请教是否有更优的解决方案?

优化方案

方案一:结合ConcurrentHashMap做原子校验 + CopyOnWriteArrayList维护顺序

利用ConcurrentHashMap的原子性操作做重复判断,避免全局锁,同时保留CopyOnWriteArrayList的有序性:

private final ConcurrentHashMap<String, Boolean> existChecker = new ConcurrentHashMap<>();
private final CopyOnWriteArrayList<String> fruits = new CopyOnWriteArrayList<>();

public boolean add(String fruit) {
    // putIfAbsent是原子操作,返回null表示元素之前不存在
    Boolean existed = existChecker.putIfAbsent(fruit, Boolean.TRUE);
    if (existed == null) {
        fruits.add(fruit);
        return true;
    }
    return false;
}

核心优势是用ConcurrentHashMap的原子操作替代同步块,避免全局锁竞争。唯一需要注意:极端情况下如果putIfAbsent成功后、fruits.add前发生异常,会出现map有记录但列表无元素的情况,可根据业务场景做补偿处理。

方案二:LinkedHashSet加显式锁(适合读多写少场景)

LinkedHashSet天然支持有序+无重复,套一层显式锁即可实现线程安全,比CopyOnWriteArrayList的写操作性能更优(避免数组复制开销):

private final LinkedHashSet<String> fruits = new LinkedHashSet<>();
private final ReentrantLock lock = new ReentrantLock();

public boolean add(String fruit) {
    lock.lock();
    try {
        // LinkedHashSet的add方法本身会判断重复,返回是否新增成功
        return fruits.add(fruit);
    } finally {
        lock.unlock();
    }
}

// 读操作如需线程安全,同样要加锁或返回快照
public List<String> getFruits() {
    lock.lock();
    try {
        return new ArrayList<>(fruits);
    } finally {
        lock.unlock();
    }
}

方案三:自定义锁保护的ArrayList(适合写多读少场景)

如果对内存开销敏感,可基于ArrayList+ReentrantLock实现,所有读写操作都在锁保护下进行:

private final List<String> fruits = new ArrayList<>();
private final ReentrantLock lock = new ReentrantLock();

public boolean add(String fruit) {
    lock.lock();
    try {
        if (fruits.contains(fruit)) {
            return false;
        }
        return fruits.add(fruit);
    } finally {
        lock.unlock();
    }
}

public List<String> getFruits() {
    lock.lock();
    try {
        return new ArrayList<>(fruits);
    } finally {
        lock.unlock();
    }
}

这种方案完全自定义逻辑,避免CopyOnWriteArrayList的数组复制开销,但读操作也需要加锁,适合写操作频率较高的场景。

内容的提问来源于stack exchange,提问作者Ahmed Bilal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 01:12:03