Java中循环删除List中元素
写在开头:最近在处理一个包装类时候,把Mapper查询到的数据暂存到了ArrayList中,后续做循环遍历删除操作的时候,当时采用了foreach循环,结果弹出了ConcurrentModificationException错误。于是乎就在博客上找解决方案,最后整理总结一下。
遍历方式:
for、foreach、iterator
常规需求:
删除一个、多个元素
准备工作:User实体类、初始化UserList
// 初始化列表
List<User> list = User.getUserList();
// 遍历列表元素
showAll(list);
结果:
展示当前列表数据
User{name='name0', age=0, address='address0'}
User{name='name1', age=1, address='address1'}
User{name='name2', age=2, address='address2'}
User{name='name3', age=3, address='address3'}
User{name='name4', age=4, address='address4'}
for
-
删除单个元素:
// for for (int i = 0; i< list.size(); i++) { if (list.get(i).getAge() == 2) { list.remove(list.get(i)); } } showAll(list);
结果:正常
User{name='name0', age=0, address='address0'} User{name='name1', age=1, address='address1'} User{name='name3', age=3, address='address3'} User{name='name4', age=4, address='address4'}
-
删除多个元素:
变更控制条件:list.get(i).getAge() > 2
结果:age=3的元素被删除,为何age=4的元素未被删除呢?
User{name='name0', age=0, address='address0'} User{name='name1', age=1, address='address1'} User{name='name2', age=2, address='address2'} User{name='name4', age=4, address='address4'}
-
总结:
List列表在删除某个元素之后,list.size()发生了变化,除此之外,元素的索引index也在发生变化,所以当删除某个元素之后,根据元素索引继续进行遍历,这样就很有可能会漏掉某些元素。因此,list.remove()方式建议删除特定元素,不建议用于删除多个元素。 -
补充说明:特定元素,具有唯一性的元素,亦或者判定条件具有唯一性
比如:我们将列表数据的age属性全部置为2,即:
User{name='name0', age=2, address='address0'} User{name='name1', age=2, address='address1'} User{name='name2', age=2, address='address2'} User{name='name3', age=2, address='address3'} User{name='name4', age=2, address='address4'}
我们再进行删除单个元素的操作会发现结果:
User{name='name1', age=2, address='address1'} User{name='name3', age=2, address='address3'}
验证了我们上述总结,我们简单叙述下遍历流程:当对元素删除之后,后续元素将整体向前移动覆盖(copy)
第一次:user:i = 0 -> name0 -> 符合条件 -> remove -> size=4 , i++ User{name='name1', age=2, address='address1'} User{name='name2', age=2, address='address2'} User{name='name3', age=2, address='address3'} User{name='name4', age=2, address='address4'} 第二次:user:i = 1 -> name2 -> 符合条件 -> remove -> size=3 , i++ User{name='name1', age=2, address='address1'} User{name='name3', age=2, address='address3'} User{name='name4', age=2, address='address4'} 第三次:user:i = 2 -> name4 -> 符合条件 -> remove -> size=2 , i++ User{name='name1', age=2, address='address1'} User{name='name3', age=2, address='address3'} 第四次:user:i = 3 -> i > list.size -> 跳出循环
foreach
-
删除单个元素**(删除多个元素同for)**
// foreach for (User user : list) { if (user.getAge() == 2) { list.remove(user); } } showAll(list);
结果:java.util.ConcurrentModificationException 异常
User{name='name0', age=0, address='address0'}
User{name='name1', age=1, address='address1'}
User{name='name2', age=2, address='address2'}
User{name='name3', age=3, address='address3'}
User{name='name4', age=4, address='address4'}
分析:
删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,抛出异常。
并发修改:list.remove() + 索引推进
解决方案:添加break,强制跳出循环遍历
// foreach
for (User user : list) {
if (user.getAge() == 2) {
list.remove(user);
break;
}
}
showAll(list);
结果:可以正常删除
User{name='name0', age=0, address='address0'}
User{name='name1', age=1, address='address1'}
User{name='name3', age=3, address='address3'}
User{name='name4', age=4, address='address4'}
iterator
迭代器遍历方式:建议使用
删除单个、多个元素:
// iterator
Iterator<User> iterator = list.iterator();
while (iterator.hasNext()) {
if (iterator.next().getAge() > 2) {
iterator.remove();
}
}
showAll(list);
结果:
User{name='name0', age=0, address='address0'}
User{name='name1', age=1, address='address1'}
User{name='name2', age=2, address='address2'}
补充说明:
采用迭代器方式进行遍历,可以正常循环以及单个、多个元素删除,需要特别注意:remove方法要使用iterator,而不是list的remove方法。
迭代器(Iterator)
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象。迭代器通常被称为“轻量级”对象。
Java中的Iterator功能比较简单,并且只能单向移动:
-
使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。
注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
-
使用next()获得序列中的下一个元素。
-
使用hasNext()检查序列中是否还有元素。
-
使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。