其實這也是老生常談的問題,但奇怪的的是最近幾年在開發時都沒遇到過,所以今天是個特殊的日子。
場景:比如後臺返回一個列表,值可能爲空,前臺自己過濾
僞代碼
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操作,哈哈