list可以一边遍历一边修改元素吗?

list可以一边遍历一边修改元素吗?

编码文章call10242025-10-01 18:45:548A+A-

List可以一边遍历一边修改元素吗?

核心答案

List 能否边遍历边修改,取决于两个因素:

  1. 集合的实现机制 (fail-fast 或 fail-safe)
  2. 遍历的方式 (增强for、Iterator、索引遍历等)

详细分析

1. fail-fast集合(ArrayList、LinkedList)

1.1 什么是fail-fast?

  • 定义 :快速失败机制,在检测到并发修改时立即抛出异常
  • 实现 :通过 modCount 计数器检测结构性修改 #技术分享
  • 目的 :避免在并发修改时产生不确定的行为

1.2 不同遍历方式的表现

增强for循环 - 不能修改

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));

for (String item : list) { if ("B".equals(item)) { list.remove(item); } }

Iterator遍历 - 有条件修改

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String item = iterator.next();
    if ("B".equals(item)) {
        // list.remove(item);  //  直接修改集合会抛异常
        iterator.remove();     //  使用Iterator的remove方法
    }
}

索引遍历 - 可以修改(需注意索引)

for (int i = 0; i < list.size(); i++) {
    if ("B".equals(list.get(i))) {
        list.remove(i);
        i--;
    }
}

for (int i = list.size() - 1; i >= 0; i--) { if ("B".equals(list.get(i))) { list.remove(i); } }

Stream forEach - 不能修改

list.stream().forEach(item -> {
    if ("B".equals(item)) {
        list.remove(item);
    }
});

1.3 fail-fast原理解析

public abstract class AbstractList<E> {

    protected transient int modCount = 0;

    private class Itr implements Iterator<E> {

        int expectedModCount = modCount;

        public E next() {
            checkForComodification();

        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

        public void remove() {

            expectedModCount = modCount;
        }
    }
}

2. fail-safe集合(CopyOnWriteArrayList)

2.1 什么是fail-safe?

  • 定义 :安全失败机制,允许在遍历时修改集合
  • 实现 :遍历的是集合的快照(副本)
  • 特点 :修改对当前遍历不可见

2.2 所有遍历方式都可以修改

增强for循环

List<String> list = new CopyOnWriteArrayList<>(Arrays.asList("A", "B", "C"));

for (String item : list) { if ("B".equals(item)) { list.remove(item); list.add("D"); } }

Iterator遍历

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String item = it.next();
    if ("B".equals(item)) {
        list.remove(item);     //  可以
        // it.remove();         //  注意:不支持Iterator.remove()
    }
}

2.3 fail-safe原理解析

public class CopyOnWriteArrayList<E> {
    private volatile Object[] array;


    public E remove(int index) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            Object[] newElements = new Object[elements.length - 1];

            setArray(newElements);
            return oldValue;
        } finally {
            lock.unlock();
        }
    }


    public Iterator<E> iterator() {
        return new COWIterator<E>(getArray(), 0);
    }

    static final class COWIterator<E> implements Iterator<E> {
        private final Object[] snapshot;

        private COWIterator(Object[] elements, int initialCursor) {
            snapshot = elements;
        }
    }
}

对比总结表

| 特性 | fail-fast (ArrayList) | fail-safe (CopyOnWriteArrayList) | | ---

| 增强 for 循环修改 | 抛异常 | 可以 | | Iterator.remove() | 支持 | 不支持 | | 直接修改集合 | 抛异常 | 可以 | | 修改可见性 | 立即可见 | 对当前遍历不可见 | | 性能 | 读写都快 | 写操作慢(复制数组) | | 内存占用 | 低 | 高(维护副本) | | 适用场景 | 单线程/读写均衡 | 读多写少的并发场景 |

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4