Java集合如何遍历删除指定元素

  • Post author:
  • Post category:java



目录


1、删除List


2、删除Set


3、删除Map


注意事项:


1、删除List

public class ListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        list.forEach(System.out::println);

        for(String str : list) {
            if ("李四".equals(str)) {
                list.remove(str);
            }
        }

        list.forEach(s -> {
            System.out.println("第1次删除后:" + list);
        });
    }
}

以上代码运行会发生并发修改异常ConcurrentModificationException,正确的方式是:

public class ListExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        
        System.out.println(list);
        

        // 方法1:迭代器遍历
        Iterator<String> iterator = list.iterator();

        while(iterator.hasNext()) {
            String s = iterator.next();

            if ("李四".equals(s)) {
                iterator.remove();
            }
        }

        System.out.println("第1次删除后:" + list);

        // 方法2:使用集合的removeIf()方法
        list.removeIf(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return "王五".equals(s);
            }
        });

        System.out.println("第2次删除后:" + list);
    }
}

2、删除Set

同样,Set也不能通过foreach删除,否则发生异常

public class SetExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();

        set.add("张三");
        set.add("李四");
        set.add("王五");

        System.out.println(set);

        // 此处会发生并发修改异常
        for(String str : set) {
            if ("张三".equals(str)) {
                set.remove(str);
            }
        }

        System.out.println("第1次删除后:" + set);

        set.removeIf("李四"::equals);

        System.out.println("第2次删除后:" + set);
    }
}

3、删除Map

public static void main(String[] args) {
    Map<Integer, String> map = new HashMap();

    map.put(1, "张三");
    map.put(2, "李四");
    map.put(3, "王五");
    map.put(4, "赵六");
        
    map.forEach((k, v) -> System.out.println(k + "," + v));

    Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();

    while(iterator.hasNext()) {
        Map.Entry<Integer, String> entry = iterator.next();
        Integer key = entry.getKey();

        if (key == 1) {
            iterator.remove();
        }
    }

    System.out.println(map);
}

注意事项:

使用普通for循环删除list里面的元素会有bug,当删除一个元素时,list的长度会减1,被删除元素的后一个元素会向前移动,导致只删了一部分符合条件的元素。

public class ListExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        list.add("张三");
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        System.out.println("删除前:" + list);

        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);

            if ("张三".equals(s)) {
                list.remove(s);
            }
        }

        System.out.println("删除后:" + list);
    }

}

以上代码的运行结果为:

删除前:[张三, 张三, 李四, 王五, 赵六]

删除后:[张三, 李四, 王五, 赵六]

原因是:

第一轮循环,i = 0,会删除第一个张三,删除之后只剩四个元素[张三, 李四, 王五, 赵六]

第二轮循环,i = 1,从第二个元素(也就是“李四”)开始判断

以上代码可以简单的改成如下,每次都比较第一个元素

public class ListExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        list.add("张三");
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        System.out.println("删除前:" + list);

        for (int i = 0; i < list.size(); i++) {
            String s = list.get(0);

            if ("张三".equals(s)) {
                list.remove(s);
            }
        }

        System.out.println("删除后:" + list);
    }

}

事实上Collection的removeIf()方法的实现也是通过Iterator的romove()方法删除元素,源码如下:

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;
}

答疑环节:

那有童鞋就要问了,foreach遍历删除会发生异常,为什么普通for循环就能正常运行呢?

因为foreach也会被编译成迭代器遍历的方式,只不过调用的是集合的remove()方法,没有调用迭代器的remove()导致的异常。

foreach编译前:

public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        list.add("张三");
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");

        System.out.println("删除前:" + list);

        for (String s : list) {
            if ("张三".equals(s)) {
                list.remove(s);
            }
        }
        
        System.out.println("删除后:" + list);
    }

foreach编译后:

public static void main(String[] args) {
    List<String> list = new ArrayList();
    list.add("张三");
    list.add("张三");
    list.add("李四");
    list.add("王五");
    list.add("赵六");
    System.out.println("删除前:" + list);
    Iterator var2 = list.iterator();

    while(var2.hasNext()) {
        String s = (String)var2.next();
        if ("张三".equals(s)) {
            list.remove(s);
        }
    }

    System.out.println("删除后:" + list);
}

如果还想更深入了解为什么会发生异常,可以参考文章

Java集合的快速失败原则

文章总结:

使用迭代器的remove()方法删除就可以了



版权声明:本文为heyl163_原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。