应该说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 ? get(i)==null : 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()时就报错了,当然删除后立即退出则不会报错。