遍历顺序列表问题
遍历顺序表分为通过下标访问和通过迭代器访问两种方式
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 异常。