碰到一个项目中bug
今天,在项目中碰到一个Bug,这个Bug是因为对List内的元素采用错误的删除操作方法造成的。一个非常简单的错误,但写代码的时候可能常常会不小心就进了坑里。
看看下面的代码:
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<Student>() {{
add(new Student("1", "Bob"));
add(new Student("2", "Nancy"));
add(new Student("3", "Billy"));
add(new Student("4", "Dade"));
}};
final List<Student> result = deleteListElement(students);
for (Student student : result) {
System.out.println(student.getId());
}
}
private static List<Student> deleteListElement(List<Student> list) {
for (Student student : list) {
if (student.getId().equals("4")) {
list.remove(student);
}
}
return list;
}
这段代码会抛出以下异常:
java.util.ConcurrentModificationException
哪些方法可以出现避免出现这种异常呢?
迭代器的iterator.remove() 删除方法实际上调用的就是list.remove() 方法,但是迭代器在调用之前,会调用checkForComodification() 方法检查两个值modCount 和 expectedModCount 是否相等:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
通过查看源码实现,可以知道两个值的作用:
modCount 是AbstractList中的成员变量,ArrayList继承了这个变量。它表示该集合实际被修改的次数。expectedModCount 是ArrayList中的内部类Itr 中的成员变量。它表示这个迭代器期望该集合被修改的次数。这个值是在iterator() 方法被调用的时候被赋值的。只有通过迭代器对集合进行操作,该值才会跟着改变。
使用迭代器的方式如下:
private static List<Student> deleteListElement(List<Student> list) {
Iterator<Student> iterator = list.iterator();
while (iterator.hasNext()) {
if (iterator.next().getId().equals("4")) {
iterator.remove();
}
return list;
}
-
使用Java 8中util包下的removeIf方法
private static List<Student> deleteListElement(List<Student> list) {
list.removeIf(student -> student.getId().equals("4"));
return list;
}
这种写法非常简洁。 其实,点开源码的方法实现,你会看到内部实现原理也是采用的迭代器。
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过滤
Java 8中的Stream流的功能非常强大。其中filter提供了过滤的功能,可以把不符合条件的元素去除,留下需要的元素。
private static List<Student> deleteListElement(List<Student> list) {
List<Student> result = list.stream().filter(
student -> !student.getId().equals("4")
).collect(Collectors.toList());
return list;
}
其实,如果我们要删除的元素在集合中只有一个的话,那么也是可以使用增强for循环的。做法是,只要在删除之后,立刻使用break结束循环体即可,不再继续遍历集合。
private static List<Student> deleteListElement(List<Student> list) {
for (Student student : list) {
if (student.getId().equals("4")) {
ConcurrentModificationException
list.remove(student);
break;
}
}
return list;
}
总结
以上就是几种删除List中元素的方法。这些方法可以帮助我们在平时编写代码的时候,更不容易出错,写出带有Bug的代码。
|