在遍及集合过程中对集合进行删除操作最好不要做,如果非要做可以利用迭代器,并发集合,或者同步代码。
单线程模式下直接使用迭代器提供的remove方法就行或者首先记录下标遍历完后进行删除,多线程模式下建议使用同步或并发结合。
1 面试遇到的问题和迭代器没关系考察的是普通的for循环和foreach循环
下面先说出观点:
普通for循环遍历集合过程中进行删除,如果进行大量删除会报IndexOutOfBoundsException异常,如果少量删除可以成功删除,但是循环的次数会减少,造成结果不准确。
增强for循环遍历过程中进行删除,会报ConcurrentModificationException异常,这个异常大家应该很熟悉,并发修改异常。集合遍历时进行增删操作都需要留意是否会触发ConcurrentModificationException异常
下面写个简单的例子
public static void main(String[] args) {
ArrayList<String> datalist = new ArrayList<String>();
for(int i=0;i<100;i++) {
datalist.add(i+"stringdata");
}
int num = 0;
Iterator<String> itea = datalist.iterator();
//删除不会出错
while(itea.hasNext()) {
num++;
if(num %2 ==0) {
String str = itea.next();
itea.remove();
}
}
int count = 0;
//少量删除不会报错,大量删除会报indexoutofbounds
for(int i = 0;i<datalist.size();i++) {
count++;
if(i== 5) {
datalist.remove(i);
System.out.println("delete "+i+" "+datalist.get(i)+"");
}
}
System.out.println("执行次数 "+count);
num = 0;
//删除就会出错
for(String data :datalist) {
num++;
if(num == 5) {
datalist.remove(data);
}
}
}
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at com.ldx.demo2.Test4.main(Test4.java:41)
2 为什么迭代器删除不会出错
集合都实现了自己的迭代器,从ArrayList进行迭代器的分析,ArrayList..iterator()获取迭代器,
迭代器不会出错原因:迭代器内部还是利用ArrayList的添加删除函数进行操作,只不过操作只有会对相应的指针进行修改(下一个),如果进行了删除操作,集合整体长度变小,指向下一个的指针也会相应减小,所以再次访问下一个时就不会发生错误了。
//获取迭代器
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;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
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];
}
//重点看remove操作
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();
}
}
//modeCount 利用集合的操作可以修改,expectedModCount 则只有利用迭代器才能修改
//如果操作之前两个值不一样,就会报异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
arrayList remove函数
public E remove(int index) {
rangeCheck(index);
//修改modCount
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;
}
看源码还可以发现ListIterator,是一个功能更加强大的Iterator,只能用于各种List类型的访问,ListIterator特点。
(1)可以向前,向后遍历.
(2)产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引.
(3)可以使用set()方法替换它访问过的最后一个元素.
(4)可以使用add()方法在next()方法返回的元素之前或previous()方法返回的元素之后插入一个元素.