ArrayList源码相对简单,这个数据结构的底层就是一个动态数组,在扩容时使用System类的copy方法。初始化容量为 0 在添加元素时会将数组扩容到10,以后每一次扩容均扩容1.5倍。线程不安全的容器,多线程环境不能使用。
这里说两个需要注意的点,之前面试的时候遇到过。
1、ArrayList在执行删除操作之后,被删除元素后面的元素都会向前移动一位,我在这里贴一下源码:
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);
//将最后一个元素置为null
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
因为会自动向前进行移动所以在 for 循环 执行删除的时候,会出现意想不到的结果。当然执行 foreach 删除也不行,会报错:java.util.ConcurrentModificationException。这是因为在这里,foreach循环遍历容器本质上是使用迭代器进行遍历的,会对修改次数modCount进行检查,不允许集合进行更改操作,但是删除的方法还是使用的ArrayList的remove()方法,导致modCount修改,最终抛出异常。源码如下:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
如果想在遍历时执行删除,那就只有使用迭代器。使用迭代器遍历删除时,能够避免ConcurrentModificationException。这是因为:在ArrayList中,modCount是指集合的修改次数,当进行add或者delete时,modCount会+1;expectedModCount是指集合的迭代器的版本号,初始值是modCount,但是当集合进行add或者delete操作时,modCount会+1,而expectedModCount不会改变,所以会抛出异常。但是迭代器remove操作时,会同步expectedModCount的值,把modCount的值赋予expectedModCount。所以不会抛出异常。迭代器.remove的源码如下:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
//对expectedModCount进行更新
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
2、Arrays工具类中的一个方法:asList(T... a) 它是将数组转换成ArrayList的
public class Arrays {
@SafeVarargs
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
}
小心,这里的ArrayList可不是 java.util;包中的类,这个是:Arrays工具类中的一个内部类
public class Arrays {
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
}
}
可以看到它也继承AbstractList也拥有ArrayList中的抽象方法,但是这个内部类是没有将这些方法实现的,我们再看一下AbstractList中的add方法
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
一目了然,直接抛出异常。也就是我们在使用这个工具类转换出来的ArrayList时是不能执行添加和删除操作的。只能执行修改操作
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
3、总结:
1、删除时需要注意元素会自动向前移动;
2、foreach删除是语法糖,本质上还是使用的迭代器,会出现ConcurrentModificationException异常;
3、删除时尽量使用迭代器。
我是巴哥,我一定能进大厂!