目录
4. 2.用一个Collection对象来构造,并将该集合的元素添加到ArrayList
5.2 再调用ensureExplictCapacity方法
1.ArrayList基础知识
ArrayList是Java集合框架中的一个重要的类,在 java.util 中( 使用需要引入)。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
它继承于AbstractList,实现了List接口。是一个长度可变的集合,提供了增删改查的功能。
ArrayList类实现了RandomAccess接口,可以对元素进行快速访问。
实现了Serializable接口,说明ArrayList可以被序列化,还有Cloneable接口,可以被复制。
集合中允许null的存在。和Vector不同的是,ArrayList不是线程安全的。
2.ArrayList的常用方法
- public int size() 返回集合的长度。
- public E get(index index) 获取索引位置的元素
- public boolean remove(object) 删除指定的元素,返回是否删除成功
- public E remove(int index) 删除指定索引位置的元素, 返回被删除的元素
- public E set(int index, E element)修改指定索引位置元素为传入的元素,返回被修改元素
3.主要成员变量
//默认的容量大小(常量)
private static final int DEFAULT_CAPACITY = 10;
//定义的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//定义的默认空容量的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//定义的不可被序列化的数组,实际存储元素的数组
transient Object[] elementData;
//数组中的元素的个数
private int size;
4.三种初始化
4.1.默认的构造器,将会以默认的大小来初始化内部的数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
4. 2.用一个Collection对象来构造,并将该集合的元素添加到ArrayList
public ArrayList(Collection<? extends E> c) {
//将构造方法中的参数转换成数组形式,
elementData = c.toArray();
//将数组的长度赋值给size
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
//检查elementData是不是object[]类型,
//不是的话将其转换成object[].class类型
if (elementData.getClass() != Object[].class)
//数组的创建与拷贝
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
//size为0,则把已创建好的空数组直接给它
this.elementData = EMPTY_ELEMENTDATA;
}
}
4.3.用户指定大小来初始化内部数组
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//如果传进来的变量>0,则初始化一个指定容量的空数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//则不去创建新的数组,直接将已创建的EMPTY_ELEMENTDATA空数组传给Arraylist
this.elementData = EMPTY_ELEMENTDATA;
} else {
//<0,则抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
}
}
5.扩容机制(以无参构造创建)
5.1开始于往集合中添加元素方法,即 add方法(有两种)
public boolean add(E e) {
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++;
}
阅读上面代码,可以发现两种add()方法都调用了ensureCapacityInternal(size+1)方法
参数minCapacity:size+1:(把数组长度+1以确保能存下下一个数据)
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
调用 ensureCapacityInternal(size+1)方法之前会调用到下面的 calculateCapacity()方法
此时的minCapacity为1(size在初始化时为0,size+1=1)
注意:1.minCapacity为变量只是第一次调用 add方法时值为1,
此后调用需要根据实际的数组长度size+1
2.只有在使用无参构造创建数组时,才会返回DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组
在第一次添加值时,会进入到if判断,
最终会返回DEFAULT_CAPACITY固定值(即:10)。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//返回默认的容量和传入参数的较大值
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
5.2 再调用ensureExplictCapacity方法
private void ensureExplicitCapacity(int minCapacity) {
/*
* modCount表示list已经被结构化修改的次数,主要用在iterator()和listIterator()方法中。
* 因为add属于结构化的修改,所以这里modCount会加1
* */
modCount++;
if (minCapacity - elementData.length > 0)
//调用grow进行扩容
grow(minCapacity);
}
如果minCapacity>elementData.length ,会调用到扩容核心grow方法。
注意:
这块的elementData.length返回的是当前数组的容量,而不是数组的实际长度size
如果使用4.3构造创建数组
就需要根据实际情况判断是否调用grow方法。
如果使用无参构造:第一次add时,满足条件,进入grow方法
5.3进入扩容核心grow方法
private void grow(int minCapacity) {
//旧容量
int oldCapacity = elementData.length;
//将新容量变为旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//检查新容量是否大于最小需要容量minCapacity,
if (newCapacity - minCapacity < 0)
//若还是小于最小需要容量,就把最小需要容量当做数组的的新容量
newCapacity = minCapacity;
//若大于最大容量,
if (newCapacity - MAX_ARRAY_SIZE > 0)
//则新容量为Integer.MAX_VALUE,
// //若还是小于最小需要容量,就把最小需要容量当做数组的的新容量
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
int newCapacity = oldCapacity + (oldCapacity >> 1)此行代码即为扩容的核心,oldCapacity为原来的容量,右移一位,即除以2,因此这句的意思就是新的容量:即原来的1.5倍。
然后判断newCapacity如果小于minCapacity时,将minCapacity赋值给newCapacity。
对于使用无参构造时,elementData.length为0,
所以oldCapacity也为0,minCapacity为10,因此最终newCapacity为10。
在进行判断newCapacity是否大于设定的MAX_ARRAY_SIZE,
5.4 再调用hugeCapacity方法
如果minCapacity大于MAX_ARRAY_SIZE,则返回Integer的最大值,
否则返回MAX_ARRAY_SIZE
最后,通过Arrays.copyOf方法把原数组的内容放到更大容量的数组里面。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}