ArrayList 简述
- Java ArrayList是List接口的可调整大小的数组实现,这意味着它以默认大小开始,并在将更多数据添加到数组列表时自动扩容。 除了实现List接口之外,此类还提供了一些方法来操作内部用于存储列表的数组的大小。
- ArrayList不是线程安全的,多线程环境下可以考虑用 List list = Collections.synchronizedList(new ArrayList(…)); 函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。
- ArrayList 的iterator和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出ConcurrentModificationException。 因此,在并发修改的情况下,迭代器快速失败,而不是在未来的未确定时间冒任意,非确定性的风险。
ArrayList 类图
- ArrayList 继承了 AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。
- ArrayList 实现了 RandmoAccess 接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的在ArrayList中,我们即可以通过元素的索引快速获取元素对象;这就是快速随机访问。
- ArrayList 实现了 Cloneable接口,即覆盖了方法clone(),能被克隆。
- ArrayList 实现了 java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
- ArrayList与Vector不同,ArrayList 中的操作不是线程安全的。所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。
私有字段说明
//序列化版本号
private static final long serialVersionUID = 8683452581122892189L;
//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//用于空实例的共享空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
*用于默认大小的空实例的共享空数组实例。 我们将此与EMPTY_ELEMENTDATA区分开来,以便在添加
*第一个元素时知道要膨胀多少
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存储ArrayList元素的数组缓冲区。ArrayList 的容量是此数组缓冲区的长度。 添加第一个元素
* 时,任何带有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空 ArrayList 都将
* 扩展为 DEFAULT_CAPACITY。
*/
transient Object[] elementData; // 非私有以简化嵌套类访问
//ArrayList的大小(它包含的元素的数量)
private int size;
ArrayList 构造函数
ArrayList 提供了三个构造函数,分别如下:
/**
*ArrayList 默认构造函数.构造一个初始容量为10的空列表
*/
public ArrayList() {
// 替换为空数组。
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 构造具有指定初始容量的空列表。
*
* @param initialCapacity 列表的初始容量
* @throws IllegalArgumentException 如果指定的初始容量为负数
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//新建一个指定容量的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表。
*
* @param c要将其元素放入此列表的集合
* @throws NullPointerException 如果指定集合为 null
*/
public ArrayList(Collection<? extends E> c) {
this.elementData = c.toArray();
if ((this.size = this.elementData.length) != 0) {
if (this.elementData.getClass() != Object[].class) {
this.elementData = Arrays.copyOf(this.elementData, this.size, Object[].class);
}
} else {
// 替换为空数组。
this.elementData = EMPTY_ELEMENTDATA;
}
}
ArrayList常用方法
添加
public boolean add(E e)
将指定的元素追加到此列表的末尾。
public boolean add(E e) {
//扩大容量,修改modcount
ensureCapacityInternal(size + 1); // Increments modCount!!
//数组是从0开始的存元素的,而数组个数是从1开始计数的
//这里是往第size个位置上存元素
//再将元素个数加1
elementData[size++] = e;
return true;
}
//确保容量本身
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);
}
//计算数组容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果elementData为空,则设置容量大小为 Math.max(DEFAULT_CAPACITY, minCapacity);
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
public void add(int index,E element)
将指定元素插入此列表中的指定位置。 将当前位于该位置的元素(如果有)和后续元素向右移动
public void add(int index, E element) {
//下标检查,是否越界了
rangeCheckForAdd(index);
//扩增容量,同时改变modcount
ensureCapacityInternal(size + 1); // Increments modCount!!
//index后面的元素后移
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//指定位置放置元素
elementData[index] = element;
//元素数量大小自增
size++;
}
public boolean addAll(Collection<? extends E> c)
将指定集合中的所有元素按指定集合的迭代器返回的顺序附加到此列表的末尾。 如果在操作正在进行时修改了指定的集合,则此操作的行为是不确定的
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
public boolean addAll(int index, Collection<? extends E> c)
从指定位置开始,将指定集合中的所有元素插入此列表。 将当前位置的元素(如果有)和后续元素向右移动。 新元素将按照指定集合的迭代器返回的顺序出现在列表中
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
删除
public E remove(int index)
删除此列表中指定位置的元素。 将任何后续元素向左移动
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;
}
public boolean remove(Object o)
从此列表中删除指定元素的第一个匹配项(如果存在)。 如果列表不包含该元素,则不会更改
public boolean remove(Object o) {
if (o == null) {//空对象
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
// 遍历ArrayList,找到元素o,删除并返回true。
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
//快速删除第index个元素并且不返回删除的值。
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
}
public boolean removeAll(Collection<?> c)
从此列表中删除指定集合中包含的所有元素
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
//批量删除元素
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// 保留与AbstractCollection的行为兼容性,即使c.contains()抛出异常。
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
public void clear()
从此列表中删除所有元素。 此调用返回后,列表将为空
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
修改
public E set(int index, E element)
用指定的元素替换此列表中指定位置的元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
查找
public E get(int index)
返回此列表中指定位置的元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
//检查给定索引是否在范围内。 如果不是,则抛出适当的运行时异常。 此方法不检查索引是否为负数:如果索引为负,则抛出ArrayIndexOutOfBoundsException。
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//根据给定的索引获取数组elementData中对应位置的元素
E elementData(int index) {
return (E) elementData[index];
}
总结
- ArrayList可以存放null
- ArrayList本质上就是一个elementData数组
- ArrayList区别于数组的地方在于能够自动扩展大小,当ArrayList容量不足以容纳全部元素时,ArrayList会重新设置容量:新的容量=原有容量+(原有容量/2)
- ArrayList中removeAll(Collection c)和clear()的区别就是removeAll可以删除批量指定的元素,而clear是全是删除集合中的元素
- ArrayList基于数组实现,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低