一、集合元素遍历
在遍历集合的时候,我们可能会根据业务的需要而需要排除集合的一些元素,通常我们遍历List集合常用的有下列3种方式:
1.普通for循环
for(int i= 0; i<list.size(); i++) {
}
2.增强for
for(int i : list){
}
3.迭代器
Iterator <Integer> iterator = list.iterator();
while (iterator.hasNext()){}
二、集合元素的删除
综合上面三种方式测试删除,代码如下:
@Test
public void test3(){
try {
delete1();
}catch (Exception e){
log.info("delete1 方法遍历删除出现异常,异常类型是{}",e.getClass().getSimpleName());
}
try {
delete2();
}catch (Exception e){
log.info("delete2 方法遍历删除出现异常,异常类型是{}",e.getClass().getSimpleName());
}
try {
delete3();
}catch (Exception e){
log.info("delete3 方法遍历删除出现异常,异常类型是{}",e.getClass().getSimpleName());
}
}
// 初始化一个list
public List<Integer> init(){
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
return list;
}
// 普通for循环删除
public <T> void delete1(){
List<Integer> list = init();
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals(3)){
list.remove(i);
}
}
log.info("delete1 方法执行成功");
}
// 迭代器删除
public void delete2(){
List<Integer> list = init();
Iterator <Integer> iterator = list.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
if (next.equals(3)) {
iterator.remove();
}
}
log.info("delete2 方法执行成功");
}
// 增强for循环
public void delete3(){
List<Integer> list = init();
for (Integer t : list) {
if (t.equals(3)) {
list.remove(t);
}
}
log.info("delete3 方法执行成功");
}
我们来看一下控制台输出的结果:
21:03:01.697 [main] INFO com.kuake.concurrent.DemoTest - delete1 方法执行成功
21:03:01.701 [main] INFO com.kuake.concurrent.DemoTest - delete2 方法执行成功
21:03:01.701 [main] INFO com.kuake.concurrent.DemoTest - delete3 方法遍历删除出现异常,异常类型是ConcurrentModificationException
三、测试结果
>普通for删除: 删除正常
>增强for删除: 删除时抛出异常ConcurrentModificationException
>迭代器删除: 删除正常
四、异常探究
为什么使用增强for循环会抛出这个异常呢。首先我来看看增强for遍历编译之后的代码是怎么样。通过idea找到对应的class
文件。
public void delete3() {
List<Integer> list = this.init();
Iterator var2 = list.iterator();
while(var2.hasNext()) {
Integer t = (Integer)var2.next();
if (t.equals(3)) {
list.remove(t);
}
}
log.info("delete3 方法执行成功");
}
原来我们的迭代器,在编译之后,jvm也是把它翻译成了使用迭代器进行遍历。那么我们就可以怀疑是不是迭代器遍历的时候有什么要求呢?当翻阅ArrayList
的迭代器源码时,看到了如下代码。代码片段如下:这是他的next()
方法,
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
上述方法中一个checkForComodification()
代码如下:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
modCount 是表示list集合的修改次数,expectedModCount是合理操作时的逾期修改次数;正常操作时modCount==expectedModCount
**原因:**是因为list.remove()
方法会使得modCount++操作;而expectedModCount保存的还是原始值。然后当执行迭代器的 next()操作时就会抛出上述异常。
五、迭代器的remove()方法
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
迭代器的remove方法 会将执行expectedModCount = modCount
操作。所以在下一次执行next()方法就不会抛出异常。
六、小结
推荐使用 迭代器的remove()方法进行集合元素删除操作。