一、ArrayList数据结构
ArrayList底层是数组实现的,数组元素的类型是Object类型,可以动态的增长和缩减。
ArrayList先继承AbstractList,AbstractList实现List接口
二、ArrayList的属性
//版本号
private static final long serialVersionUID = 8683452581122892189L;
//初始化默认值
private static final int DEFAULT_CAPACITY = 10;
//指定ArrayList容量为10时,返回该空对象数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//调用无参构造方法时,返回该空对象数组。它与EMPTY_ELEMENTDATA最大的区别是:该数组是默认返回的,而EMPTY_ELEMENTDATA是用户指定容量为0时返回的
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存储元素数组
transient Object[] elementData;
//元素大小
private int size;
//最大数组容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
三、构造方法
ArrayList有三个构造方法
(1)ArrayListint (int initialCapacity)
/*指定一个初始容量为initialCapacity的空ArrayList*/
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);
}
}
(2)ArrayList()
/* 构造一个初始容量为 10 的空列表 */
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
(3)ArrayList(Collection<? extends E> c)
/*构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();//将集合转为数组
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
四、核心方法
(1)add(E e):将元素添加到列表的尾部
public boolean add(E e) {
//判断容量是否够用
ensureCapacityInternal(size + 1); // Increments modCount!!
//在正确的位置上存入元素e,并且size++
elementData[size++] = e;
return true;
}
//数组容量检查,不够时进行扩容,minCapacity为需要的最小容量
public void ensureCapacity(int minCapacity) {
//如果elementData等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA则最小扩容为10,否则为0
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
//如果需要的最小容量大于最小扩容容量,则使用需要的最小容量
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
//数组容量检查,不够时进行扩容
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
//数组容量检查,不够时扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//若最小容量大于数组缓冲区的当前长度,则进行扩容
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);//扩容
}
//扩容
private void grow(int minCapacity) {
//获取当前数组的容量
// overflow-conscious code
int oldCapacity = elementData.length;
//进行扩容,新的容量=旧的容量+旧容量/2
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果扩容后的容量小于需要的最小容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;//将扩容后的容量再次扩容为需要的最小的容量
//如果扩容后的容量大于最大的临界值,则进行大容量分配
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);//大容量分配
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
//进行大容量分配
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
//如果需要的容量大于MAX_ARRAY_SIZE,分配 Integer.MAX_VALUE 否则分配MAX_ARRAY_SIZE
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
总结:
add(E e)方法进行扩容的步骤:
①先进行空间检查,看是否需要扩容,若需要扩容,确定最少需要扩容的容量
②确定扩容后,执行grow(int minCapacity)方法,minCapacity为最小需要扩容的容量
③第一次扩容, newCapacity = oldCapacity + (oldCapacity >> 1);在原有容量基础上增加一半
④第一次扩容后,判断容量是否还小于需要的最小扩容容量minCapacity,若还小于,将容量扩充为minCapacity
⑤对扩容后的容量进行判断,如果扩容后的容量大于最大的临界值,则进行大容量分配。如果需要的容量大于MAX_ARRAY_SIZE,分配 Integer.MAX_VALUE 否则分配MAX_ARRAY_SIZE
(2)add(int index,E element):在指定位置添加元素
public void add(int index, E element) {
rangeCheckForAdd(index);//判断是否越界
//确定容量,若不够,进行扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//对数组进行复制,空出index的位置插入加入的元素
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将index位置赋值为性加入的元素
elementData[index] = element;
//容量+1
size++;
}
//越界检查
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
总结:
①越界检查,若越界抛出异常
②确定容量,判断容量是否足够,不够进行扩容
③移动元素,腾出需要插入元素的空间,插入元素
(3)remove(int index):删除指定位置的元素
public E remove(int index) {
rangeCheck(index);//越界检查
//结构性修改次数+1
modCount++;
E oldValue = elementData(index);//将要删除的元素值存放在oldValue
int numMoved = size - index - 1;//计算删除元素后需要移动的元素个数
if (numMoved > 0)//如果需要移动的元素个数大于0,则需要把要删除元素之后的所有元素向左移动
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将要删除的元素置null,size-1
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
总结:
删除指定位置的元素:
①越界检查,若越界抛出异常
②计算需要移动的元素个数,元素个数大于0,则需要把要删除元素之后的所有元素向左移动,覆盖掉要删除的元素
③删除的元素赋值为null
(4)remove(Object 0):移除此列表中首次出现的指定元素
public boolean remove(Object o) {
//找到需要删除元素的index值,用fastRemove(index)方法进行删除
if (o == null) {//此处证明ArrayList可以有null值
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
//结构性修改次数+1
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
}
(5)set(int index,E element):用指定的元素替代此列表中指定位置上的元素
public E set(int index, E element) {
//检查是否越界
rangeCheck(index);
E oldValue = elementData(index);//将被替换的元素存放在oldValue中
elementData[index] = element;//用新元素替换旧元素
return oldValue;
}
(6)get(int index):返回此列表中指定位置上的元素
public E get(int index) {
//检查是否越界
rangeCheck(index);
//返回索引为index的元素
return elementData(index);
}
//返回索引为index的元素
E elementData(int index) {
return (E) elementData[index];
}
总结:
ArrayList底层本质是elementData数组,在数据的查询方面会很快,但是在增加、删除方面相对慢点,因为有可能需要移动大量的元素。ArrayList初始容量为10,根据需求会进行相应的扩容。
ArrayList和Vector的区别:
①ArrayList是线程不安全的,Vector是线程安全的(synchronized),当多条线程访问同一个ArrayList集合时,需要手动保证该集合的同步性,而Vector则不用。
②ArrayList和Vector都采用线性连续存储空间,当存储空间不足的时候,ArrayList默认增加为原来的50%(newCapacity = oldCapacity + (oldCapacity >> 1)),Vector默认增加为原来的一倍(newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity))