一:首先看下幾個ArrayList循環過程删除元素的方法(一下内容均基于jdk7):
package list;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.prefs.Preferences;
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("a1", "ab2", "a3", "ab4", "a5", "ab6", "a7", "ab8", "a9"));
// 疊代删除方式一
for (String str : list) {
System.out.println(str);
if (str.contains("b")) {
list.remove(str);
}
}
// 疊代删除方式二
int size = list.size();
for (int i = 0; i < size; i++) {
String str = list.get(i);
System.out.println(str);
if (str.contains("b")) {
list.remove(i);
// size--;
// i--;
}
}
// 疊代删除方式三
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
System.out.println(str);
if (str.contains("b")) {
list.remove(i);
}
}
// 疊代删除方式四
for (Iterator<String> ite = list.iterator(); ite.hasNext();) {
String str = ite.next();
System.out.println(str);
if (str.contains("b")) {
ite.remove();
}
}
// 疊代删除方式五
for (Iterator<String> ite = list.iterator(); ite.hasNext();) {
String str = ite.next();
if (str.contains("b")) {
list.remove(str);
}
}
}
}
方式一:報錯 java.util.ConcurrentModificationException
方式二:報錯:下标越界 java.lang.IndexOutOfBoundsException
list移除了元素但size大小未響應變化,是以導緻數組下标不對;
list.remove(i)必須size--
而且取出的資料的索引也不準确,同時需要做i--操作
方式三:正常删除,不推薦;每次循環都需要計算list的大小,效率低
方式四:正常删除,推薦使用
方式五:報錯: java.util.ConcurrentModificationException
二:如果上面的結果算錯的話,先看下ArrayList的源碼(add和remove方法)
ArrayList繼承AbstractList,modCount是AbstractList中定義用于計算清單的修改次數的屬性。
public class ArrayList<E> extends AbstractList<E> // AbstractList定義了:protected transient int modCount = 0;
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
//設定arrayList預設容量
private static final int DEFAULT_CAPACITY = 10;
//空數組,當調用無參數構造函數的時候預設給個空數組,用于判斷ArrayList資料是否為空時
private static final Object[]EMPTY_ELEMENTDATA = {};
//這才是真正儲存資料的數組
private transient Object[] elementData;
//arrayList的實際元素數量
private int size;
//構造方法傳入預設的capacity 設定預設數組大小
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
this.elementData = new Object[initialCapacity];
}
//無參數構造方法預設為空數組
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
//構造方法傳入一個Collection, 則将Collection裡面的值copy到arrayList
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
//下面主要看看ArrayList 是如何将數組進行動态擴充實作add 和 remove
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
public void add(int index, E element) {
rangeCheckForAdd(index);
System.arraycopy(elementData, index, elementData, index + 1,size - index);
elementData[index] = element;
size++;
private void ensureCapacityInternal(int minCapacity) {
// 通過比較elementData和EMPTY_ELEMENTDATA的位址來判斷ArrayList中是否為空
// 這種判空方式相比elementData.length更友善,無需進行數組内部屬性length的值,隻需要比較位址即可。
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
ensureExplicitCapacity(minCapacity);
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//ArrayList每次資料更新(add,remove)都會對modCount的值更新
//超出了數組可容納的長度,需要進行動态擴充
if (minCapacity - elementData.length > 0)
grow(minCapacity);
//這才是ArrayList動态擴充的點
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//設定新數組的容量擴充為原來數組的1.5倍,oldCapacity >>1 向右位移,相當于oldCapacity/2, oldCapacity + (oldCapacity >> 1)=1.5*oldCapacity
int newCapacity = oldCapacity + (oldCapacity >> 1);
//再判斷一下新數組的容量夠不夠,夠了就直接使用這個長度建立新數組,
//不夠就将數組長度設定為需要的長度
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//判斷有沒超過最大限制
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//将原來數組的值copy新數組中去, ArrayList的引用指向新數組
//這兒會新建立數組,如果資料量很大,重複的建立的數組,那麼還是會影響效率,
//是以鼓勵在合适的時候通過構造方法指定預設的capaticy大小
elementData = Arrays.copyOf(elementData, newCapacity);
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
// 删除方法
public boolean remove(Object o) {
// Object可以為null
if (o == null) {
// 如果傳入的對象是null,則會循環數組查找是否有null的元素,存在則拿到索引index進行快速删除
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
// 對象非空則通過循環數組通過equals進行判斷,最終還是要通過fastRemove根據索引删除
if (o.equals(elementData[index])) {
return false;
// 快速删除方法:基于下标進行準确删除元素
private void fastRemove(int index) {
// 删除元素會更新ArrayList的modCount值
modCount++;
// 數組是連續的存儲資料結構,當删除其中一個元素,該元素後面的所有的元素需要向前移動一個位置
// numMoved 表示删除的下标到最後總共受影響的元素個數,即需要前移的元素個數
int numMoved = size - index - 1;
if (numMoved > 0)
// 在同一個數組中進行複制,把(删除元素下标後面的)數組元素複制(拼接)到(删除元素下标前的)數組中
// 但是此時會出現最後那個數組元素還是以前元素而不是null
System.arraycopy(elementData, index+1, elementData, index,numMoved);
// 經過elementData[--size] = null則把數組删除的那個下标移動到最後,加速回收
elementData[--size] = null; // clear to let GC do its work
三:看下ArrayList進行foreach時所調用的疊代器(内部疊代器Itr)
/**
* An optimized version of AbstractList.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
// expectedModCount是Itr特有的,modCount是公共的
// expectedModCount和modCount預設是兩者相等的;ArrayList進行删除修改都會更新modCount的值
// 當ArrayList通過foreach進入它的内部疊代器Itr時,expectedModCount就被指派為modCount的值,後續ArrayList進行增加或删除,隻會更新modCount,而不會同步更新expectedModCount
// 是以疊代器根據這兩個值進行判斷是否有并發性修改
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
// ArrayList通過foreach(即增強for循環)來循環是調用的是ArrayList中内部類Itr的next()
@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];
// ArrayList中疊代器删除方法
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
// 通過ArrayList中foreach(即通過ArrayList内部Itr的疊代器)進行删除元素
// 此時會進行指派 expectedModCount = modCount;而不會抛出異常
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
final void checkForComodification() {
if (modCount != expectedModCount)
對此應該差不多可以了解了。ArrayList通過foreach疊代是調用的其内部類Itr的next方法。如果通過foreach循環,要去除某些元素,隻能通過疊代器删除。因為疊代器删除後會對expectedModCount = modCount設定,不會再循環過程因為expectedModCount 和 modCount值不相等而抛出異常了。如果是通過ArrayList的删除則隻會對modCount進行更新,但是ArrayList内部疊代器Itr的屬性expectedModCount卻沒有得到更新,是以抛異常。