ArrayList 内部存储结构是Object类型的数组。构造ArrayList时,如果传入初始大小,那它将新建一个指定大小的数组,否则建空数组。查找、修改速度快,增加、删除速度慢,ArrayList是非线程安全的。
增(添加):仅是将这个元素添加到末尾。操作快速。
增(插入):由于需要移动插入位置后面的元素,并且涉及数组的复制,所以操作较慢。
删:由于需要将删除位置后面的元素向前挪动,也会设计数组复制,所以操作较慢。
改:直接对指定位置元素进行修改,不涉及元素挪动和数组复制,操作快速。
查:直接返回指定下标的数组元素,操作快速。
//增(添加)
public boolean add(E e) {
//添加前先检查是否需要拓展数组, 此时数组长度最小为size+1
ensureCapacityInternal(size + 1);
//将元素添加到数组末尾
elementData[size++] = e;
return true;
}
//增(插入)
public void add(int index, E element) {
//插入位置范围检查
rangeCheckForAdd(index);
//检查是否需要扩容
ensureCapacityInternal(size + 1);
//挪动插入位置后面的元素
System.arraycopy(elementData, index, elementData, index + 1, size - index);
//在要插入的位置赋上新值
elementData[index] = element;
size++;
}
//删
public E remove(int index) {
//index不能大于size
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0) {
//将index后面的元素向前挪动一位
System.arraycopy(elementData, index+1, elementData, index, numMoved);
}
//置空引用
elementData[--size] = null;
return oldValue;
}
//改
public E set(int index, E element) {
//index不能大于size
rangeCheck(index);
E oldValue = elementData(index);
//替换成新元素
elementData[index] = element;
return oldValue;
}
//查
public E get(int index) {
//index不能大于size
rangeCheck(index);
//返回指定位置元素
return elementData(index);
}
动态扩容
每次添加数据前,都会进行容量检查,如果是空数组,那么新建默认大小10的数组,否则就检查是否满足最小容量,不满足就调用grow方法扩容,每次扩容都为原来数组长度的一半,实际是建立一个新的更大的数组,将原来的数据全部复制到新数组。
private void ensureCapacityInternal(int minCapacity) {
//如果此时还是空数组
if (elementData == EMPTY_ELEMENTDATA) {
//和默认容量比较, 取较大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//数组已经初始化过就执行这一步
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//如果最小容量大于数组长度就扩增数组
if (minCapacity - elementData.length > 0) {
grow(minCapacity);
}
}
//集合最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//增加数组长度
private void grow(int minCapacity) {
//获取数组原先的容量
int oldCapacity = elementData.length;
//新数组的容量, 在原来的基础上增加一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
//检验新的容量是否小于最小容量
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
//检验新的容量是否超过最大数组容量
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = hugeCapacity(minCapacity);
}
//拷贝原来的数组到新数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
面试常问题
ArrayList和Vector区别
- Vector方法都是同步的(方法都用synchronized修饰),而ArrayList是非线程安全的(List<Map<String,Object>> data=Collections.synchronizedList(new ArrayList<Map<String,Object>>()) 可以解决ArrayList线程安全问题)
- Vector扩容的时候,容量会增加两倍,而ArrayList只会增加1.5
- Vector可以设置容量增长的参数,ArrayList不可以
ArrayrList和LinkedList的区别?
- ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
- 对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针,数组的时间复杂度是1,链表是n。
- 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据,数组的时间复杂度是n,链表是1
ArrayList用来做队列合适么数组呢
- 队列一般是FIFO(先入先出)的,如果用ArrayList做队列,就需要在数组尾部追加数据,数组头部删除数组,反过来也可以。但是无论如何总会有一个操作会涉及到数组的数据搬迁,这个是比较耗费性能的。
- 数组是非常合适的。比如ArrayBlockingQueue内部实现就是一个环形队列,它是一个定长队列,内部是用一个定长数组来实现的。简单点说就是使用两个偏移量来标记数组的读位置和写位置,如果超过长度就折回到数组开头,前提是它们是定长数组。