ArrayList循环遍历删除元素常见陷阱



应该说ArrayList在我们日常开发中使用到的频率是非常高的,因为它作为Array的增强型,提供了很多操作方法。既然常用,那么我们就更有必要仔细的认识它一下,以防在使用的时候出现错误,这里,我主要和大家讨论学习一下循环遍历下remove方法应该注意的地方。

ArrayList的remove方法主要是重写了AbstractList的remove方法,而AbstractList的remove方法则是实现了List接口的remove方法。

remove(index):

   

/**

* Removes the element at the specified position in this list.

* Shifts any subsequent elements to the left (subtracts one from their

indices)

*/

public E remove(int index) {

rangeCheck(index);

modCount++;

E oldValue = elementData(index);

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

return oldValue;

}

remove(object):

/**

* Removes the first occurrence of the specified element from this list,

* if it is present. If the list does not contain the element, it is

* unchanged. More formally, removes the element with the lowest index

* <tt>i</tt> such that

* <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>

* (if such an element exists). Returns <tt>true</tt> if this list

* contained the specified element (or equivalently, if this list

* changed as a result of the call).

*

* @param o element to be removed from this list, if present

* @return <tt>true</tt> if this list contained the specified element

*/

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 remove method that skips bounds checking and does not

* return the value removed.

*/

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

}

从源码可以看出两个方法大致都做了三件事:

   1.    modCount++

   2.    将目标点后面的前移

   3.    置空最后空出的位置,使之垃圾回收


     

public static void main(String... args){

List<String> list = new ArrayList<>();

list.add("a");

list.add("b");

list.add("b1");

list.add("c");

list.add("c");

list.add("d");

forIndexDelete(list);

//forEachDelete(list);

//iteratorDelete(list);

System.out.println(list.toString());

}

/**

* 一般for遍历并删除

*

* @param lis

*/

private static void forIndexDelete(List<String> list) {

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

//由于i每次循环都增1,所以填补被remove元素位置的元素不会再次被读取到

//解决办法:1. remove后i回退1

// 2. 倒序遍历for(int i = list.size()-1; i>=0; i--)

System.out.println(list.get(i));

if ("c" == list.get(i)) {

list.remove(i);//元素被移除,后面的都前移一位

//i--;

}

}

}

/**

* forEach方式遍历并删除

*

* @param list

*/

private static void forEachDelete(List<String> list) {

//在成功删除后的下一次循环时会抛CuncurrentModificationException

for (String str : list) {

//等同于 for(Iterator i = list.iterator; i.hasNext(); )

System.out.println(str);

if (str == "c") {

list.remove(str);//if not break -- ConcurrentModificationException

//break;

}

}

}

/**

* Iterator遍历删除

*

* @param list

*/

private static void iteratorDelete(List<String> list) {

Iterator iterator = list.iterator();

while (iterator.hasNext()) {

String str = (String) iterator.next();

System.out.println(str);

if (str == "c") {

iterator.remove();//没有任何问题

}

}

}


漏删很好理解,就是下标跳过了嘛,但是为啥在forEach的情况就会报ConcurrentMordificationException、Iterator下remove则没有问题,下面我们来看源码:

首先看看ArrayList的iterator()方法都干些啥,

public Iterator<E> iterator() {
        return new Itr();
    }
private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;
  。。。

public E next() {

checkForComodification();

int i = cursor;

if (i >= size)

throw new NoSuchElementException();

Object[] elementData = ArrayList.this.elementData;

if (i >= elementData.length)

throw new ConcurrentModificationException();

cursor = i + 1;

return (E) elementData[lastRet = i];

}

public void remove() {

if (lastRet < 0)

throw new IllegalStateException();

checkForComodification();

try {

ArrayList.this.remove(lastRet);

cursor = lastRet;

lastRet = -1;

expectedModCount = modCount;

} catch (IndexOutOfBoundsException ex) {

throw new ConcurrentModificationException();

}

}

final void checkForComodification() {

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

}


返回了一个Iterator的实现类Itr,Itr实现了remove(), next()等方法,注意两个remove方法,ArrayList.remove和Itr.remove的主要区别在于后者做了expectedModCount = modCount;  这个就很重要了。再看看方法next(),里面的checkForComodification()方法,做了个判断,如果不相等就抛异常。所以forEach在删除元素后进入下次循环调用next()时就报错了,当然删除后立即退出则不会报错。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值