遍历顺序列表问题

遍历顺序表分为通过下标访问和通过迭代器访问两种方式

 

1、 下标访问:只要下标不超过列表的size大小,就不会抛出IndexOutOfBoundsException;

public static void main(String[] args) {
        List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8);
        int size = list.size();
        for (int i = 0; i< size; ++i) {
            Integer e = list.get(i);
            if (e == 6) {
                list.remove(e);
            }
        }
}
// 报错抛IndexOutOfBoundsException异常

public static void main(String[] args) {
        List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8);
        for (int i = 0; i< list.size(); ++i) {
            Integer e = list.get(i);
            if (e == 6) {
                list.remove(e);
            }
        }
}
// 正常,因为循环条件每次都会重新计算列表size,而列表size会随着列表的add和remove操作做自增或自减

 

2、 迭代器访问:若迭代器在遍历列表元素,同时有新元素被添加到列表中或者旧元素从列表中删除,则抛出ConcurrentModificationException;

public static void main(String[] args) {
        List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8);
        Iterator<Integer> it = list.iterator();
        while (it.hasNext()) {
            Integer e = it.next();
            if (e == 6) {
                list.remove(e);
            }
        }
}
// 报错抛ConcurrentModificationException,因为每次对列表做add或remove操作,列表中的变量modCount会自增,而迭代器中存在变量expectedModCount,其在初始化时被赋予和modCount相同的值。
// 迭代器遍历列表时,首先判断expectedModCount与modCount是否一致,若不相等则说明列表在迭代器创建后被篡改了,故抛出ConcurrentModificationException异常。

public static void main(String[] args) {
        List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8);
        ListIterator<Integer> it = list.listIterator()
        while (it.hasNext()) {
            Integer e = it.next();
            if (e == 6) {
                it.remove();
                it.add(6 * 2);
            }
        }
}
// 正常,迭代器做add或remove操作时,先判断expectedModCount与modCount是否一致,再通过列表做add或remove操作,然后将expectedModCount重新设置为和modCount一致。
// 因此遍历下一个元素时不会抛出ConcurrentModificationException。

 

3、 增强for循环访问:本质上还是迭代器访问

public static void main(String[] args) {
        List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8);
        for (Integer e : list) {
            if (e == 6) {
                list.remove(e);
            }
        }
}
// 报错,at java.util.ArrayList$Itr.checkForComodification抛出ConcurrentModificationException 异常。

 

4、 stream流访问:本质上还是迭代器访问

public static void main(String[] args) {
        List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8);
        List<Integer> result = list.stream().map(e -> {
            list.add(e * 2);
            return e;
        }).collect(Collectors.toList());
}
// 报错,at java.util.ArrayList$ArrayListSpliterator.forEachRemaining抛出ConcurrentModificationException 异常。

 

因此,若有其它线程向列表中添加元素,那么当前线程遍历列表可能会有抛异常的风险。

public class SpringServiceContext {
    protected static ExecutorService executorService;
    static {
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("other-process-thread-%d").build();
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(1, 1, 0,
                TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(1000), threadFactory, new ThreadPoolExecutor.AbortPolicy());
        poolExecutor.prestartAllCoreThreads();
        executorService = poolExecutor;
    }

    public static void main(String[] args) {
        List<Integer> list = Lists.newArrayList();
        CompletableFuture.runAsync(() -> {
            for (int i = 1; I < 20000; ++i) {
                list.add(i);
                try {
                    Thread.sleep(10);
                } catch (Exception e) {}
            }
        },executorService);

        list.stream().map(e -> {
            try {
                Thread.sleep(10);
            } catch (Exception e2) {}
            return e;
        }).collect(Collectors.toList());
    }
}
// 报错,at java.util.ArrayList$ArrayListSpliterator.forEachRemaining抛出ConcurrentModificationException 异常。

 

posted @ 2022-08-09 15:03  thePacer  阅读(99)  评论(0编辑  收藏  举报