其实这也是老生常谈的问题,但奇怪的的是最近几年在开发时都没遇到过,所以今天是个特殊的日子。
场景:比如后台返回一个列表,值可能为空,前台自己过滤
伪代码
ArrayList<People> strings = new ArrayList<People>();
strings.add(new People(""));
strings.add(new People(""));
strings.add(new People(""));
strings.add(new People("a"));
for (int i = 0; i < strings.size(); i++) {
People people = strings.get(i);
if (people.name == null || people.name == "") {
strings.remove(people);
}
System.out.println(i + "----" + people.name + "------" + strings.size());
}
0----------3
1----------2
strings.get(i)==
strings.get(i)==a
有一个符合条件的没删掉!!!
冷静下,看下源码
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
主要是因为内部size做了-1的操作,遍历到下一节点时,index其实相当于把第二个空值跳过了直接删除了第三个。这种情况可以使用迭代器操作
ListIterator<People> it = strings.listIterator();
while (it.hasNext()) {
People people = it.next();
if (people.name == null || people.name == "") {
it.remove();
}
System.out.println( people.name + "------" + strings.size());
}
------3
------2
------1
a------1
strings.get(i)==a
顺便看看看其他坑,使用for each遍历
for (People people : strings) {
if (people.name == null || people.name == "") {
strings.remove(people);
}
System.out.println(people.name + "------" + strings.size());
}
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
报了个错,因为foreach遍历本质就是利用iterater的hasNext和next结合进行遍历 看这。
iterater在创建时会把modCount赋值给expectedModCount,但增删的时候不会更新expectedModCount。
现在遇到这两种情况,应该还会有其他的,比如老版本中调用remove可能造成下标越界,因为size没有实时调整,所以有个不成文的规定,循环操作里尽量不要做remove操作,哈哈