當要刪除ArrayList里面的某個(gè)元素,一不注意就容易出bug。今天就給大家說(shuō)一下在A(yíng)rrayList循環(huán)遍歷并刪除元素的問(wèn)題。首先請看下面的例子:
import java.util.ArrayList;
public class ArrayListRemove
{
public static void main(String[] args)
{
ArrayList
list.add('a');
list.add('b');
list.add('b');
list.add('c');
list.add('c');
remove(list);
for (String s : list)
{
System.out.println('element : ' + s);
}
}
public static void remove(ArrayList
{
}
}
常見(jiàn)錯誤寫(xiě)法:
舉例一:
結果:第二個(gè)“b”的字符串沒(méi)有刪掉。
舉例二:
結果:這種for-each寫(xiě)法會(huì )報出并發(fā)修改異常:java.util.ConcurrentModificationException。
錯誤原因:先看下ArrayList中的remove方法,看入參為Object的remove方法是怎么實(shí)現的:
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size;="">
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size;="">
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
執行路徑會(huì )到else路徑下最終調用faseRemove方法:
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; // Let gc do its work
}
可以看到會(huì )執行System.arraycopy方法,導致刪除元素時(shí)涉及到數組元素的移動(dòng)。針對錯誤寫(xiě)法一,在遍歷第一個(gè)字符串b時(shí)因為符合刪除條件,所以將該元素從數組中刪除,并且將后一個(gè)元素移動(dòng)(也就是第二個(gè)字符串b)至當前位置,導致下一次循環(huán)遍歷時(shí)后一個(gè)字符串b并沒(méi)有遍歷到,所以無(wú)法刪除。針對這種情況可以倒序刪除的方式來(lái)避免:
Java
因為數組倒序遍歷時(shí)即使發(fā)生元素刪除也不影響后序元素遍歷。
實(shí)例二的錯誤原因。產(chǎn)生的原因卻是foreach寫(xiě)法是對實(shí)際的Iterable、hasNext、next方法的簡(jiǎn)寫(xiě),問(wèn)題同樣處在上文的fastRemove方法中,可以看到第一行把modCount變量的值加一,但在A(yíng)rrayList返回的迭代器:
這里返回的是AbstractList類(lèi)內部的迭代器實(shí)現private class Itr implements Iterator,看這個(gè)類(lèi)的next方法:
Java
第一行checkForComodification方法:
這里會(huì )做迭代器內部修改次數檢查,因為上面的remove(Object)方法修改了modCount的值,所以才會(huì )報出并發(fā)修改異常。要避免這種情況的出現則在使用迭代器迭代時(shí)(顯示或for-each的隱式)不要使用ArrayList的remove,改為用Iterator的remove即可。
public static void remove(ArrayList
{
Iterator
while (it.hasNext())
{
String s = it.next();
if (s.equals('b'))
{
it.remove();
}
}
}
聯(lián)系客服