在Java中删除集合中元素时,需要特别注意,一方面是因为,Java是并发编程语言,如果存在并发操作,则会出现逻辑异常;另一方面,在List中删除元素,如果操作不当,则会抛出异常。
阿里推出的《Java开发手册》中的编程规范要求:不要在 foreach 循环里进行元素的 remove/add 操作。对应描述如下:
【强制】不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator
方式,如果并发操作,需要对 Iterator 对象加锁。
正例:
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素的条件) {
iterator.remove();
}
}
反例:
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的结果吗?
Collection中删除元素
Collection中删除元素时,可以直接调用remove方法。但是,如果在forEach循环中删除元素,则会报错。错误示例如下:
public void removeElement() {
List<String> list = new LinkedList<>();
list.add("1");
list.add("2");
list.add("3");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
}
直觉上,上述代码不会有问题,就是在遍历List的时候,删除指定元素。但是,这种方法将会导致运行时异常。异常堆栈如下图所示。
查看异常堆栈,发现是执行checkForComodification校验时,抛出并发修改异常。对应代码如下:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
也就是说,在forEach中增加或删除元素时,会进行并发修改校验。所以,不应在forEach中对Collection执行元素增加/删除操作。如果需要在forEach中增加或删除元素,可以使用Iterator,示例代码如下:
public void removeElement() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if ("1".equals(item)) {
iterator.remove();
}
}
}
Java 8 以后,Collection新增removeIf用来实现循环中元素删除。示例代码如下:
public void removeElement() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.removeIf(item -> "1".equals(item));
}
分析removeIf可知,其实现就是使用Iterator。源码如下:
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
当然,也可使用java 8的Stream接口的filter方法来过滤并生成新Collection的方式,此处不再赘述,有兴趣的同学可以自行练习使用。
Map中删除元素
与Collection一样,在Map中删除元素时,可以直接调用remove方法。但是,如果在forEach循环中删除元素,则会报错。错误示例如下:
public void removeElement() {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
map.entrySet().forEach(entry -> {
if("1".equals(entry.getKey())){
map.remove("1");
}
});
}
执行上述代码抛出的异常和使用forEach遍历并删除Collection中元素抛出异常一致,这里不再重复说明map中同样可以通过Iterator在Map中删除元素。示例代码如下:
public void removeMapElement() {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<String, String> entry = it.next();
if(entry.getKey() == "1") {
it.remove();
}
}
}
参考
《Java开发手册》 嵩山版 阿里巴巴
https://blog.csdn.net/hu18315778112/article/details/124321602 List集合for循环删除元素
https://www.cnblogs.com/cchilei/p/13099203.html map循环删除某个元素