底层的数据结构就是数组,数组元素类型为Object类型,便可以存放全部类型数据。
咱们对ArrayList类的实例的全部的操做底层都是基于数组的。java
ArrayList继承的父类为:AbstractList(抽象类)
实现的接口有:List(规定了List的操做规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)数组
友情提示:由于ArrayList实现了RandomAccess接口,因此尽可能用for(int i = 0; i < size; i++) 来遍历而不要用Iterator迭代器来遍历,后者在效率上要差一些数据结构
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
类的属性中核心的属性为elementData,类型为Object[],用于存放实际元素,而且被标记为transient,也就意味着在序列化的时候,此字段是不会被序列化的。dom
友情提示:ArrayList的默认容量为10函数
// 缺省容量 private static final int DEFAULT_CAPACITY = 10; // 元素数组(调用指定初始值的构造函数时elementData的长度会变成指定值) transient Object[] elementData; // 空对象数组 private static final Object[] EMPTY_ELEMENTDATA = {}; // 缺省空对象数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
友情提示:建立ArrayList时尽可能设置初始大小(使用ArrayList(int initialCapacity)构造函数)源码分析
/** * ArrayList带容量大小的构造函数 * * @param initialCapacity */ //说明:指定elementData数组的大小,不容许初始化大小小于0,不然抛出异常。 public ArrayList(int initialCapacity) { if (initialCapacity > 0) {// 初始容量大于0 this.elementData = new Object[initialCapacity];// 初始化元素数组(新建一个数组) } else if (initialCapacity == 0) {// 初始容量为0 this.elementData = EMPTY_ELEMENTDATA;// 为空对象数组 } else {// 初始容量小于0,抛出异常 throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); } } /** * ArrayList无参构造函数。默认容量是10 */ //说明:当未指定初始化大小时,会给elementData赋值为空集合。 public ArrayList() { // 无参构造函数,设置元素数组为空 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
友情提示:
add方法:当容量到达size时进行扩容(add(E e)中先调用了ensureCapacity(size+1)方法,以后将元素的索引赋给elementData[size],然后size自增),扩容为当前容量的0.5倍(若果ArrayList的当前容量为10,那么一次扩容后的容量为15)
set方法:调用set方法时会检验索引是否合法(只校验了上限)(index不能等于size(index<size))
get方法:调用get方法时也会检验索引是否合法(只校验了上限)(index不能等于size(index<size))
remove方法:在移除指定下标的元素时,会把指定下标到数组末尾的元素向前移动一个单位,而且会把数组最后一个元素设置为null,这样是为了方便以后整个数组不被使用时,能够被GC;元素移动时使用的是System.arraycopy()方法测试
/** * 添加元素 * * @param e * @return */ public boolean add(E e) { //确保elementData数组有合适的大小 ensureCapacityInternal(size + 1); elementData[size++] = e; return true; } /** * 设定指定下标索引的元素值 * * @param index * @param element * @return */ public E set(int index, E element) { // 检验索引是否合法 rangeCheck(index); // 旧值 E oldValue = elementData(index); // 赋新值 elementData[index] = element; // 返回旧值 return oldValue; } /** * 获取指定下标的元素 * * @param index * @return */ //说明:get函数会检查索引值是否合法(只检查是否大于size,而没有检查是否小于0), // 值得注意的是,在get函数中存在element函数,element函数用于返回具体的元素 public E get(int index) { // 检验索引是否合法 rangeCheck(index); return elementData(index); } // Positional Access Operations /** * 位置访问操做 * * @param index * @return */ //说明:返回的值都通过了向下转型(Object -> E(泛型)) @SuppressWarnings("unchecked") E elementData(int index) { return (E) elementData[index]; } /** * 移除指定下标元素 * * @param index * @return */ //说明:remove函数用户移除指定下标的元素 // 此时会把指定下标到数组末尾的元素向前移动一个单位,而且会把数组最后一个元素设置为null,这样是为了方便以后将整个数组不被使用时,能够会被GC。 //提醒:元素移动时使用的是System.arraycopy()方法 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); // 赋值为空,有利于进行GC elementData[--size] = null; // clear to let GC do its work // 返回旧值 return oldValue; } /** * 在指定下标位置插入元素 * @param index * @param element */ 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++; }
友情提示:rangeCheckForAdd方法用于add(int index, E element)和addAll(int index, Collection<? extends E> c)方法中检验索引是否合法;rangeCheck方法用于get、set等方法中检验索引是否合法(由于不改变数据结构,故index不能取到size,最大只能取到size-1)this
//检验索引是否合法(只校验了上限)(index不能等于size(index<size)) private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //检验索引是否合法(校验了上限和下限)(index能够等于size(0<=index<=size)) private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
// 肯定ArrarList的容量。 // 若ArrayList的容量不足以容纳当前的所有元素,设置 新的容量=“(原始容量x3)/2 + 1” public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;//默认容量10 if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } //确保elementData数组有合适的大小 private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 判断元素数组是否为空数组 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);// 取较大值 } //确保elemenData数组有合适的大小 ensureExplicitCapacity(minCapacity); } //确保elemenData数组有合适的大小 private void ensureExplicitCapacity(int minCapacity) { // 结构性修改加1 modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } //对数组进行扩容 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length;// 旧容量 int newCapacity = oldCapacity + (oldCapacity >> 1);// 新容量为旧容量的1.5倍 if (newCapacity - minCapacity < 0)// 新容量小于参数指定容量,修改新容量 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0)// 新容量大于最大容量 newCapacity = hugeCapacity(minCapacity);// 指定新容量 // 拷贝扩容 elementData = Arrays.copyOf(elementData, newCapacity); }
public class AddTest { @Test public void test1(){ //咱们能够看到,在add方法以前开始elementData = {}; // 调用add方法时会继续调用,直至grow,最后elementData的大小变为10, // 以后再返回到add函数,把8放在elementData[0]中 List<Integer> lists = new ArrayList<Integer>(); lists.add(8); } @Test public void test2(){ //说明:咱们能够知道,在调用add方法以前,elementData的大小已经为6,以后再进行传递,不会进行扩容处理。 List<Integer> lists = new ArrayList<Integer>(6);//elementData.length=6 lists.add(8); } }
public class RangeCheckTest { @Test public void test() { List list = new ArrayList(); list.add(1); list.add(2); list.add(3); //该语句报ArrayIndexOutOfBoundsException异常是rangeCheck(index)引起的(index >= size) System.out.println(list.get(10)); //rangeCheck(index)方法只校验上线,该语句报ArrayIndexOutOfBoundsException异常是elementData[index]引起的 System.out.println(list.get(-1)); list.remove(-1);//同上 Object[] a = new Object[]{1, 2, 3}; System.out.println(a[-1]); } }
public class IndexOfTest { @Test public void test(){ List list = new ArrayList(); list.add(null); list.add(2); list.add(2); list.add(null); System.out.println(list.indexOf(null));//0 System.out.println(list.indexOf(2));//1 System.out.println(list.indexOf(3));//-1 } }
public class ToArrayTest { @Test public void test() { List list = new ArrayList(10); list.add(1); list.add(2); list.add(3); list.add(4); Object[] array =list.toArray(); //调用Arrays.copyOf()-->调用System.arraycopy() } }