集合中元素删除

在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的时候,删除指定元素。但是,这种方法将会导致运行时异常。异常堆栈如下图所示。
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循环删除某个元素

猜你喜欢

转载自blog.csdn.net/wangxufa/article/details/124368898