Arrays
首先我们看看Arrays类。Arrays类提供了将数据转为集合的方法asList()
Arrays.asList()
List<Integer> arrayList = Arrays.asList(0, 0, 4, 2, 5, 0, 3, 0);
arrayList.add(10);
System.out.println(arrayList);
执行add操作后会出现异常?
我们去看看Arrays的源码
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
根据这个方法的官方注释我们可以知道Arrays.asList返回的是大小固定的列表。
而且这里的ArrayList并不是java.util包下的ArrayList而是Arrays的一个内部类。
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
这个ArrayList和util包下的ArrayList一样都集成了AbstractList
但是不同的是这个List并没有重写AbstractList中的add和remove方法
AbstractList中的add方法
public boolean add(E e) {
add(size(), e);
return true;
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
这样,上述提出的问题就能得到解释。
再来看看ArrayList的remove操作
public class Test {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(0, 0, 4, 2, 5, 0, 3, 0));
remove(arrayList,0);
System.out.println(arrayList);
}
public static void remove(ArrayList<Integer> list, Integer elem) {
// 方法一:普通for循环正序删除,删除过程中元素向左移动,不能删除重复元素(具有不确定性)
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals(elem)) {
list.remove(i);
}
}
// 方法二:普通for循环倒序删除,删除过程中元素向左移动,可以删除重复的元素
for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i).equals(elem)) {
list.remove(i);
}
}
// 方法三:增强for循环删除,使用ArrayList的remove()方法删除,产生并发修改异常 ConcurrentModificationException
for (Integer str : list) {
if (str.equals(elem)) {
list.remove(str);
}
}
// 方法四:迭代器,使用ArrayList的remove()方法删除,产生并发修改异常 ConcurrentModificationException
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
if(iterator.next().equals(elem)) {
list.remove(iterator.next());
}
}
// 方法五:迭代器,使用迭代器的remove()方法删除,可以删除重复的元素
Iterator iterator2 = list.iterator();
while (iterator2.hasNext()) {
if(iterator2.next().equals(elem)) {
iterator2.remove();
}
}
}
}
方法三和方法四都抛出了ConcurrentModificationException这个异常,我们要了解这个异常首先要了解modCount。这个变量第一在AbstractList中
protected transient int modCount = 0;
根据注释可以看出他记录的是List结构修改(add、remove)的次数。这是变量在iterator中会用到。
ArrayList的add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
在执行elementData[size++]=e之前首先会执行ensureCapacityInternal(size+1)方法
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
这个方法是在判断是否需要扩容(ArrayList是基于动态数组实现的)可以看到在判断的时候对modCount++。
所以每当我们给list添加一个元素时就会执行一次++操作,根据我们的代码此时modCount=8数据的大小size=8
再看看Iterator的具体实现类Itr(ArrayList的内部类)
private class Itr implements Iterator<E> {
int cursor; // 下一个要返回元素的索引
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
//是否有下一个元素取决于cursor不等于size最开始我们的cursor=0
//size=8
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
//当expectedModCount != modCount;会抛出
//ConcurrentModificationException异常
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();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
当我们得到Iterator的具体对象时cursor=0,expectedModCount = modCount=8
hasNext()当cousor!=size的时候就证明有下一个元素
next()会先判断expectedModCount 和modCount是否相等
ArrayList的remove方法有两个先看看List如何定义?
/**
* Removes the element at the specified position in this list (optional
* operation). Shifts any subsequent elements to the left (subtracts one
* from their indices). Returns the element that was removed from the
* list.
*
* @param index the index of the element to be removed
* @return the element previously at the specified position
* @throws UnsupportedOperationException if the <tt>remove</tt> operation
* is not supported by this list
* @throws IndexOutOfBoundsException if the index is out of range
* (<tt>index < 0 || index >= size()</tt>)
*/
E remove(int index);
删除此列表中指定位置的元素(可选操作)。将任何后续元素向左移动(从其索引中减去一个)。返回从列表中删除的元素。
/**
* Removes the first occurrence of the specified element from this list,
* if it is present (optional operation). If this 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
* @throws ClassCastException if the type of the specified element
* is incompatible with this list
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* list does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws UnsupportedOperationException if the <tt>remove</tt> operation
* is not supported by this list
*/
boolean remove(Object o);
从该列表中删除第一个出现的指定元素(可选操作)。如果此列表不包含元素,则它是不变的。更正式地说,删除索引最低的元素
再看看ArrayList中的具体实现
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
//判断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;
}
/**
* 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 {
//将o与集合中的元素进行比较,如果相等移除
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
可以看到不管是哪个remove方法都对modCount进行++操作
remove(Object o)是在fastRemove()方法中进行了++
所以当我们使用了list.remove方法后modCount和expectedModCount就会不相等。
避免此异常的方法
- 使用iterator遍历时,需要修改集合则使用iteratoer自己的remove方法(因为在iteratoer中会有expectedModCount = modCount;操作)
- 使用for、foreach循环,使用list的remove方法(但是可能会有所误差)
- 记录要删除元素的下表,最后一次删除
最后ArrayList的两个remove方法你能区别吗?
public static void main(String[] args) {
List<Integer> arrayList = new ArrayList<>(Arrays.asList(0, 0, 4, 2, 5, 0, 3, 0));
Integer k = 0;
while (k < arrayList.size()) {
if (arrayList.get(k) == 0) {
arrayList.remove(k);
}
k++;
}
System.out.println(arrayList);
}
输出结果为[0, 2, 0, 3, 0]
public static void main(String[] args) {
List<Integer> arrayList=new ArrayList<>(Arrays.asList(0,0,4,2,5,0,3,0));
int k=0;
while(k<arrayList.size()){
if(arrayList.get(k)==0){
arrayList.remove(k);
System.out.println(k+" "+arrayList);
}
k++;
}
System.out.println(arrayList);
}
输出结果为[0, 4, 2, 5, 3]
参考博客:
https://blog.csdn.net/Jiangshan11/article/details/83038857
https://www.jianshu.com/p/a7df6082ec00