一、前言
我们在日常开发中经常遇到循环里面删除数据的情况,但是如果我不注意使用方式的话很容易造成删除问题。我们学习下循环删除数据的正确方式。
二、普通for循环
package learn;
import java.util.ArrayList;
import java.util.List;
/**
* @author qx
* @date 2024/2/5
* @des
*/
public class ForDemo {
public static void main(String[] args) {
List<String> list = initData();
for (int i = 0; i < list.size(); i++) {
// 删除列表第2个和第3个数据
if (i == 1 || i == 2) {
list.remove(i);
}
}
System.out.println(list);
}
private static List<String> initData() {
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
return list;
}
}
执行结果:
[aa, cc, ee]
我们的需求是要删除数据bb和cc但是却删除了bb和dd。原因就是我们删除第一个元素之后,集合重新进行了排序,打乱了之前的数据排序,删除bb后 顺序为[aa, cc,dd, ee],然后我们再删除索引为2的数据,所以就剩[aa, cc, ee]。所以这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。
三、高级for循环
package learn;
import java.util.ArrayList;
import java.util.List;
/**
* @author qx
* @date 2024/2/5
* @des
*/
public class ForDemo {
public static void main(String[] args) {
List<String> list = initData();
for (String s : list) {
if ("bb".equals(s) || "cc".equals(s)) {
list.remove(s);
}
}
System.out.println(list);
}
private static List<String> initData() {
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
return list;
}
}
执行结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
at java.util.ArrayList$Itr.next(ArrayList.java:861)
at learn.ForDemo.main(ForDemo.java:14)
其实,基本上所有的集合类都会有一个叫做快速失败的校验机制,当一个集合在被多个线程修改并访问时,就会出现ConcurrentModificationException 校验机制。它的实现原理就是我们经常提到的modCount修改计数器。如果在读列表时,modCount发生变化则会抛出ConcurrentModificationException异常。这与线程同步是两码事,线程同步是为了保护集合中的数据不被脏读、脏写而设置的。
首先Java的foreach循环其实就是根据list对象创建一个Iterator迭代对象,用这个迭代对象来遍历list,相当于list对象中元素的遍历托管给了Iterator,你如果要对list进行增删操作,都必须经过Iterator。iterator创建的时候modCount被赋值给了expectedModCount,但是调用list的add和remove方法的时候不会同时自动增减expectedModCount,这样就导致两个count不相等,从而抛出异常。
四、Iterator和removeif
iterator方式:
package learn;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author qx
* @date 2024/2/5
* @des
*/
public class ForDemo {
public static void main(String[] args) {
List<String> list = initData();
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String data = iterator.next();
if ("bb".equals(data) || "cc".equals(data)) {
iterator.remove();
}
}
System.out.println(list);
}
private static List<String> initData() {
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
return list;
}
}
执行结果正确:
[aa, dd, ee]
removeif方式
package learn;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author qx
* @date 2024/2/5
* @des
*/
public class ForDemo {
public static void main(String[] args) {
List<String> list = initData();
list.removeIf(s -> "bb".equals(s) || "cc".equals(s));
System.out.println(list);
}
private static List<String> initData() {
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
return list;
}
}
执行结果正确:
[aa, dd, ee]
五、stream流处理
package learn;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author qx
* @date 2024/2/5
* @des
*/
public class ForDemo {
public static void main(String[] args) {
List<String> list = initData();
list = list.stream().filter(s -> !"bb".equals(s) && !"cc".equals(s)).collect(Collectors.toList());
System.out.println(list);
}
private static List<String> initData() {
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
return list;
}
}
执行结果正确:
[aa, dd, ee]
六、总结
我们一般使用iterator迭代器的方式进行集合循环的删除。不考虑性能的时候使用removeIf方法,代码简洁明了。