一、案例
//案例一
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
} //运行不报错
//案例二
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
for (String item : list) {
if ("2".equals(item)) {
list.remove(item);
}
}
} //运行报错Exception in thread "main" java.util.ConcurrentModificationException
二、javap反编译后字节码分析
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String 1
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: ldc #6 // String 2
20: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
25: pop
26: aload_1
27: invokeinterface #7, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
32: astore_2
33: aload_2
34: invokeinterface #8, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
39: ifeq 72
42: aload_2
43: invokeinterface #9, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
48: checkcast #10 // class java/lang/String
51: astore_3
52: ldc #6 // String 2
54: aload_3
55: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
58: ifeq 69
61: aload_1
62: aload_3
63: invokeinterface #12, 2 // InterfaceMethod java/util/List.remove:(Ljava/lang/Object;)Z
68: pop
69: goto 33
72: return
21-25行:java编译器把for循环编译成List.iterator,然后通过Iterator.hasNext,Iterator.next遍历,然后checkcast指令String类型校验
三、源码分析
1. 调用ArrayList.iterator(),返回Iterator对象
2. 标记5是
Iterator对象的remove(),标记6是
ArrayList的remove()
3. cursor是Iterator当前指针,lastRet是Iterator上次遍历的指针,modCount是ArrayList的变更次数,当add(1),add(2),remove(1)时,modCount=3
案例一分析:
1. add(1),add(2),for遍历 : modCount=2,expectedModCount=2,cursor=0,size=2
2. Iterator.hasNext():返回true
3. Iterator.next():标记3的checkForComodification()校验成功,返回字符串“1”
4. equals=true,调用标记6的ArrayList的remove():modCount=3,expectedModCount=2,cursor=1,size=1
5. Iterator.hasNext():返回false,退出循环
总结:根本就没有遍历第二个字符串“2”
案例二分析:
1. add(1),add(2),for遍历 : modCount=2,expectedModCount=2,cursor=0,size=2
2. Iterator.hasNext():返回true
3. Iterator.next():标记3的checkForComodification()校验成功,返回字符串“1”
4. equals=false:modCount=2,expectedModCount=2,cursor=1,size=2
5. Iterator.hasNext():返回true
6. Iterator.next():标记3的checkForComodification()校验成功,返回字符串“2”
7. equals=true,调用标记6的ArrayList的remove():modCount=3,expectedModCount=2,cursor=2,size=1
8. Iterator.hasNext():返回true
9. Iterator.next():标记3的checkForComodification()校验失败,modCount != expectedModCount抛异常ConcurrentModificationException
总结:
1. ArrayList.remove()方法不会更新expectedModCount,cursor,只更改了原数组,size,modCount等数据,由于数据不一致导致校验失败。
2. Iterator.remove()方法会更新原数组,size,modCount等数据。
- 大小: 116.1 KB
- 大小: 112.2 KB
分享到:
相关推荐
在遍历 ArrayList 时,不能使用传统的 for 循环来删除元素,因为这将导致 ConcurrentModificationException 异常。相反,我们可以使用以下三种方法来遍历和删除 ArrayList 元素: ### 1. 使用迭代器(Iterator) ...
在ArrayList中,元素存储在一个连续的内存区域,通过索引可以直接访问到元素,因此对于ArrayList,使用for循环遍历是非常高效的操作,因为get方法可以通过索引快速访问元素,时间复杂度为O(1)。 但是,当使用普通...
- **遍历元素**:可以使用 `for-each` 循环或迭代器进行遍历。 - **修改元素**:`list.set(index, newValue);` 3. **ArrayList 的重要方法和属性** - **构造器**: - 默认构造器:`ArrayList()`,初始容量为16...
- **增强型for循环(foreach)遍历**(方法3):Java 5引入的语法糖,适用于实现了Iterable接口的对象,如ArrayList。 ```java for(String tmp : list){ System.out.println(tmp); } ``` - **索引遍历**...
由于ArrayList支持随机访问,所以通过for-each循环或迭代器进行遍历非常高效。`ListIterator`提供了双向遍历的功能,可以向前或向后移动。 7. **线程安全性**: ArrayList本身不是线程安全的,这意味着在多线程...
本文将深入分析Java中三种主要的遍历集合方法:传统的for循环遍历、迭代器遍历以及foreach循环遍历。 1. **传统的for循环遍历**: 这种方式依赖于计数器,开发者需要手动初始化并维护计数器,依次读取集合中的每个...
在这个例子中,使用了for循环来遍历ArrayList。循环变量`i`从0开始,直到`aa.size()`(当前集合的大小),每次迭代都会获取并打印出集合中相应位置的对象。`aa.get(i)`用于获取索引为`i`的元素,这里需要强制转换回`...
总结来说,这个练习涵盖了`ArrayList`的基本操作,包括添加、删除、遍历和排序,同时也涉及了`Iterator`的使用和性能比较。通过这个练习,你可以加深对Java集合框架的理解,尤其是`ArrayList`和`Collections`工具类...
六、遍历ArrayList 1. for-each循环:`for (E e : list) { ... }` 2. Iterator迭代器:`Iterator<E> iterator = list.iterator(); while (iterator.hasNext()) { E e = iterator.next(); ... }` 七、ArrayList的...
首先,我们需要创建一个ArrayList类,这个类将包含添加、删除、查找、更新和遍历元素等基本操作。由于JavaScript的Array对象已经提供了这些功能,我们可以通过扩展Array原型来创建ArrayList。 ```javascript ...
6. 遍历:通过for循环遍历整个列表。 7. 多级存储:ArrayList可以包含其他集合,如在上述代码中,一个ArrayList包含了另一个ArrayList。 二、ArrayList的重要属性 ArrayList的核心属性是`elementData`,这是一个...
此部分内容介绍了如何创建一个存储字符串的ArrayList集合,并通过for循环实现遍历,遍历的过程中使用`get(index)`方法获取元素,使用`size()`方法获取集合的长度。 ### ArrayList存储学生对象并遍历 此部分讲解了...
**错误的遍历删除方式** 错误的示例代码如下: ```java Set<CheckWork> set = this.getUserDao().getAll().get(0).getActionCheckWorks(); for (CheckWork checkWork : set) { if (checkWork.getState() == 1) { ...
然而,在遍历过程中直接修改`ArrayList`(如删除元素)可能会引发`IndexOutOfBoundsException`等异常。 #### 错误示例分析 以下是一种常见的错误操作方式: ```java for (int i = 0; i (); i++) { if (list.get...
可以使用for循环或增强for循环(也称for-each循环)来遍历ArrayList。此外,也可以使用Iterator或ListIterator迭代器进行遍历。 5. ArrayList的容量和扩容机制: 初始创建的ArrayList有一个默认的初始容量(通常...
首先,ArrayList的基本操作包括初始化、添加元素、删除元素、修改元素以及遍历元素。我们可以创建一个ArrayList对象,并指定其初始容量,例如: ```java ArrayList<String> list = new ArrayList(5); ``` 添加元素...
在这个场景中,我们使用ArrayList来实现用户信息的添加、删除、更新和查询功能,这在实际的业务开发中非常常见,特别是对于小型数据集的操作。 **一、添加用户信息** 添加用户信息是通过调用ArrayList的`add()`...
为了解决这些问题,Java提供了更高级的数据结构,如ArrayList和LinkedList,它们在内部实现了动态内存管理和优化的插入/删除操作,但牺牲了直接访问的效率。 总之,本章深入探讨了Java中的方法使用、数组遍历以及...
ArrayList提供了在数组中添加、删除和查找元素的便利操作,而无需预先知道数组的大小。下面,我们将深入探讨如何用C语言实现ArrayList及其相关的知识点。 首先,`Array.c`文件通常会包含ArrayList的核心实现,包括...