ArrayListList集合分析
加粗样式
前言
集合是Java的基础知识,在面试过程中经常被问到集合相关的知识,在工作中我们经常用到List和Map,List又分为ArrayList、LinkedList和Vector,本文简单的对ArrayList集合进行了讲解。
一、核心属性
首先我们看一下ArrayList有哪些属性:
- DEFAULT_CAPACITY: 默认初始容量的大小,值为10。
- EMPTY_ELEMENTDATA: 这是一个空数组,用来初始化集合内部的elementData。
- DEFAULTCAPACITY_EMPTY_ELEMENTDATA: 同样是一个空数组,用来初始化集合内部的elementData,咱也不知道为啥搞俩。
- elementData: 用来存放具体内容数据的。
- size: 记录着ArrayList当前存放数据长度。
- MAX_ARRAY_SIZE: 最大容量大小,值为int的最大值-8。
二、核心方法
下面看一下ArrayList中我们常用的的方法是怎么实现的。
构造方法:
如果是无参构造方法的话只是对elementData做了个初始化赋值。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
传递int的有参构造方法,如果大于0的话,初始化为传递参数大小的数组;如果等于0,初始化为空数组;如果小于0报异常。
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);
}
}
传递集合的构造方法,对其内部的数组进行拷贝赋值给elementData;如果传递的集合大小为0,则初始化为空数组。
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
elementData = EMPTY_ELEMENTDATA;
}
}
add()方法
add()方法是我们本次分析的重点,先看一下源码:
public boolean add(E e) {
//对数组进行扩容
ensureCapacityInternal(size + 1);
//赋值
elementData[size++] = e;
return true;
}
重点看一下扩容机制:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//如果数组大小为空的话,在默认容量和1之间去一个最大值,最终设置的默认大小为10;如果不为空的话返回的为size+1。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//用size+1与上次设置的数组的长度做判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//扩容机制
private void grow(int minCapacity) {
//记录下原来数组的大小
int oldCapacity = elementData.length;
//设置新的数组大小为原来数组大小的1.5倍,右移一位代表着除以2
int newCapacity = oldCapacity + (oldCapacity >> 1);
//新的数组大小和size+1之间取最大值,size为0和1的时候会遇到
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果数组容量比int最大值-8大,则将数组容量扩充为int的最大值
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//进行数组拷贝,扩大数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
get()方法
get()方法是取集合中的内容使用的,这个比较简单
//首先判断是否数组越界
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
remove()方法
remove()方法是集合用来溢出元素的,有传递下表和对象两种。
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;
return oldValue;
}
传递对象的移除
public boolean remove(Object o) {
if (o == 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;
}
还有size()方法,就是返回size属性这就不用说了。
三、ArrayList和Vector的区别
Vector的源码和ArrayList的没多少区别,感兴趣的自己可以自己看一下。通过看源码发现ArrayList与Vector内部都是基于数组实现的,但是两个之间有一些区别的地方是面试常问的。
- 看构造方法,ArrayList创建的是空数组,直到add的时候才初始化容量为10;而Vector在创建的时候就直接初始化数组容量为10了。
- 看扩容机制,ArrayList的扩容机制是1.5倍扩容;而Vector的扩容机制是默认二倍扩容,构造方法还可以传参指定扩容倍数。
- 在Vector中使用synchronized修饰符,其实线程安全的,当然相对的效率就低;ArrayList是线程不安全的,但是他的效率相对就高。