ArrayList概述
1)ArrayList是基于长度可动态增长的数组实现List接口的java集合类
2)ArrayList类内部维护者一个动态可再分配的Object[]数组,每一个类对象都有一个capacity属性,表示所封装的Object[]对象数组的长度,但往ArrayList添加元素时,该属性值会自动增加,体现了动态的特点
3)如果要往ArrayList里一次添加大量元素可以调用ensureCapacity方法传入满足添加元素的最终容量,减少ArrayList每次自动重分配的次数从而提高性能
4)ArrayList和vector区别是:ArrayList是异步的即线程不安全,当有多线程访问该集合时,程序要手动保证集合的同步;相反vector则是同步,线程安全的
5)ArrayList和Collection关系:
ArrayList数据结构
arrayList的数据结构:
arryList底层的数据结构就是数组,所有对ArrayList的操作都是基于对数组的操作
ArrayList源码分析
1、继承结构图
其继承结构:
ArrayList extends AbstractList
AbstractList extends AbstractCollection
分析:
1)ArrayList为什么要继承abstractList而不直接实现list接口?其实采用的就是适配器的思想,方便ArrayList的代码更简洁
2)ArrayList实现的接口
RandomAccess接口:标记性接口,用于快速存取,即实现了该接口后用for循环遍历比使用迭代器迭代的效率更高
3)Cloneable接口:可以使用Object.Clone()方法
4)Serializable接口:实现序列化接口,可以实现序列化功能保存
5)类中属性
6)构造函数
1、无参构造函数
Constructs an empty list with an initial capacity of ten. 这里就说明了默认会给10的大小,所以说一开始arrayList的容量是10.ArrayList中储存数据的其实就是一个数组,这个数组就是elementData
private transient Object[] elementData;
public ArrayList() {
super(); //调用父类中的无参构造方法,父类中的是个空的构造方法
this.elementData = EMPTY_ELEMENTDATA;//EMPTY_ELEMENTDATA:
是个空的Object[], 将elementData初始化,elementData也是个Object[]类型。
空的Object[]会给默认大小10,等会会解释什么时候赋值的。
}
2、有参构造函数
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
super(); //父类中空的构造方法
if (initialCapacity < 0) //判断如果自定义大小的容量小于0,则报下面这个非法数据异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity]; //将自定义的容量大小当成初始化elementData的大小
}
7)核心方法
1、boolean add(E);//默认直接在末尾加元素
*Appends the specified element to the end of this list.添加一个特定的元素到list的末尾。
*@param e element to be appended to this list
*@return <tt>true</tt> (as specified by {@link Collection#add}
*/
public boolean add(E e) {
//确定内部容量是否够了,size是数组中数据的个数,因为要添加一个元素,所以size+1,先判断size+1的这个个数数组能否放得下,就在这个方法中去判断是否数组.length是否够用了。
ensureCapacityInternal(size + 1); // Increments modCount!!
//在数据中正确的位置上放上元素e,并且size++
elementData[size++] = e;
return true;
}
分析:
ensureCapacityInternal(xxx);确定内部容量的方法
*
*@param
*/
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {//看,判断初始化的elementData是不是空的数组,也就是没有长度
//因为如果是空的话,minCapacity=size+1;其实就是等于1,空的数组没有长度就存放不了,所以就将minCapacity变成10,也就是默认大小,但是带这里,还没有真正的初始化这个elementData的大小。
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//确认实际的容量,上面只是将minCapacity=10,这个方法就是真正的判断elementData是否够用
ensureExplicitCapacity(minCapacity);
}
ensureExplicitCapacity(xxx);
*
*@param
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious codeminCapacity如果大于了实际elementData的长度,那么就说
elementData数组的长度不够用,不够用那么就要增加elementData的length。
minCapacity到底是什么呢,这里给你们分析一下
第一种情况:由于elementData初始化时是空的数组,那么第一次add的时候,minCapacity=size+1;也就minCapacity=1,在上一个方法(确定内部容量ensureCapacityInternal)就会判断出是空的数组,就会给将minCapacity=10,到这一步为止,还没有改变elementData的大小
第二种情况:elementData不是空的数组了,那么在add的时候,minCapacity=size+1;也就是minCapacity代表着elementData中增加之后的实际数据个数,拿着它判断elementData的length是否够用,如果lengt不够用,那么肯定要扩大容量,不然增加的这个元素就会溢出。 if (minCapacity - elementData.length > 0)
//arrayList能自动扩展大小的关键方法就在这里了
grow(minCapacity);
}
grow(minCapacity)
*
*@param
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //将扩充前的elementData大小给oldCapacity
int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity就是1.5倍的oldCapacity
if (newCapacity - minCapacity < 0)//这句话就是适应于elementData就空数组的时候,length=0,那么oldCapacity=0,newCapacity=0,所以这个判断成立,在这里就是真正的初始化elementData的大小了,就是为10.前面的工作都是准备工作。
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)//如果newCapacity超过了最大的容量限制,就调用hugeCapacity,也就是将能给的最大值给newCapacity
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//新的容量大小已经确定好了,就copy数组,改变容量大小咯。
elementData = Arrays.copyOf(elementData, newCapacity);
}
hugeCapacity()
*@param
*/
//这个就是上面用到的方法,很简单,就是用来赋最大值。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();//如果minCapacity都大于MAX_ARRAY_SIZE,那么Integer.MAX_VALUE返回,反之将MAX_ARRAY_SIZE返回。因为maxCapacity是三倍的minCapacity,可能扩充的太大了,就用minCapacity来判断了。//Integer.MAX_VALUE:2147483647MAX_ARRAY_SIZE:2147483639
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
2、void add(int , E);//在特定位置添加元素
*
*/
public void add(int index, E element) {
rangeCheckForAdd(index);//检查index也就是插入的位置是否合理。//跟上面的分析一样,具体看上面
ensureCapacityInternal(size + 1); // Increments modCount!!
//这个方法就是用来在插入元素之后,要将index之后的元素都往后移一位,
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//在目标位置上存放元素
elementData[index] = element;
size++;//size增加1
}
rangeCheckForAdd(index)
System.arraycopy(xxx)就是把elementData在插入位置后的所有元素往后面移一位
小结:
正常情况下会扩容1.5倍,特殊情况下(新扩展数组大小已经达到了最大值)则只取最大值
当我们调用add方法时,实际的函数调用
3)remove(int);//删除指定位置上的元素
*@
*/
public E remove(int index) {
rangeCheck(index);//检查index的合理性
modCount++;//这个作用很多,比如用来检测快速失败的一种标志。
E oldValue = elementData(index);//通过索引直接找到该元素
int numMoved = size - index - 1;//计算要移动的位数。
if (numMoved > 0)
//这个方法也已经解释过了,就是用来移动元素的。
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将--size上的位置赋值为null,让gc(垃圾回收机制)更快的回收它。
elementData[--size] = null; // clear to let GC do its work
//返回删除的元素。
return oldValue;
}
4)remove(Object);//从这里可以看ArrayList是可以存放null值
*
*/
//感觉这个不怎么要分析吧,都看得懂,就是通过元素来删除该元素,就依次遍历,如果有这个元素,就将该元素的索引传给fastRemobe(index),使用这个方法来删除该元素,
//fastRemove(index)方法的内部跟remove(index)的实现几乎一样,这里最主要是知道arrayList可以存储null值
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;
}
5)clear();//将elementData中每个元素赋值为null,等待垃圾回收回收
*@return <tt>true</tt> (as specified by {@link Collection#add}
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
6)set()方法
7)indexof方法
*@
// 从首开始查找数组里面是否存在指定元素
public int indexOf(Object o) {
if (o == null) { // 查找的元素为空
for (int i = 0; i < size; i++) // 遍历数组,找到第一个为空的元素,返回下标
if (elementData[i]==null)
return i;
} else { // 查找的元素不为空
for (int i = 0; i < size; i++) // 遍历数组,找到第一个和指定元素相等的元素,返回下标
if (o.equals(elementData[i]))
return i;
}
// 没有找到,返回空
return -1;
}
8)get(index)方法
总结
1)ArrayList可以存放null
2)ArrayList本质是一个elementData数组
3)ArrayList区别于一般数组是在于自动扩展大小,依赖的方法就是grow()方法
4)ArrayList由于数据结构是数组因此查询数据很快但删除和插入方面性能不高
5)ArrayList实现了RandomAccess,所以遍历时推荐使用for循环