参考:
jdk1.8.0_45源码解读——ArrayList的实现
【Java集合源码剖析】ArrayList源码剖析
从ArrayList的继承关系上看ArrayList的特点:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList实现了List接口、RandomAccess接口、Cloneable接口和Seriaizable接口。
RandomAccess接口表明ArrayList支持随机访问,ArrayList基于数组实现,实际上就是通过下标的方式获取数据
Cloneable接口表名Arraylist可以被克隆
Seriaizable接口表明ArrayList可以序列化
总结ArrayList的特点:
ArrayList基于数组实现,因此是有序的,它允许插入重复的元素和null值,支持随机访问,可以克隆和序列化,是线程不安全的。
1.属性
(1)ArrayList中默认容量为10
(2)ArrayList基于数组实现,使用elementData数组存储元素
(3)capacity与size的区别:elementData的长度(lenth)等于容量的大小,而size表示数组中实际存放元素的个数
(4)EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA的区别:EMPTY_ELEMENTDATA是空数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA虽然也是空数组,但是当首次向数组中添加元素的时候将会按照默认容量扩容。
/** * 基于JDK 1.8 */ public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8683452581122892189L; /** * ArrayList默认容量为10 */ private static final int DEFAULT_CAPACITY = 10; /** * 空的数组 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 默认容量的空数组,意思是使用无参构造函数创建ArrayList会将elementData的值设为DEFAULTCAPACITY_EMPTY_ELEMENTDATA * 当首次向数组中添加元素时,将elementData的容量设为默认容量10 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * ArrayList基于数组,用来存储数据的数组 */ transient Object[] elementData; /** * ArrayList的实际大小 */ private int size; }
2.构造函数
(1)带有初始容量参数的构造函数,如果传入的容量是0的话,使用EMPTY_ELEMENTDATA
(2)无参的构造函数,使用的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA创建空数组
/** * 带有初始容量参数构造函数 * * @param initialCapacity 初始容量 * @throws IllegalArgumentException 不合法参数异常 * */ public ArrayList(int initialCapacity) { if (initialCapacity > 0) { //创建指定容量的数组 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { //如果初始容量为0,创建一个空的数组 this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } /** * 无参构造函数,将会创建一个空数组,当首次向数组中添加元素时,会将数组的容量设为默认容量10 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 构造函数,根据传入的集合创建list */ public ArrayList(Collection<? extends E> c) { //将传入的集合转为数组 elementData = c.toArray(); if ((size = elementData.length) != 0) { // 如果c.toArray()得到的不是Object[]类型 if (elementData.getClass() != Object[].class) //使用copyOf方法拷贝elementData中的元素并转为Object[]类型,将新生成的数组返回给elementData elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // 创建空的ArrayList this.elementData = EMPTY_ELEMENTDATA; } }
3.添加和扩容方法
(1)在add方法中可以看到,每次新增元素的时候,首先会调用ensureCapacityInternal()来确保有足够的容量来添加新元素
(2)ensureCapacityInternal()和ensureCapacity()方法:ensureCapacityInternal是私有函数,在add方法中调用,ensureCapacity是公有方法,可以在外部调用该方法进行扩容。
(3)ensureExplicitCapacity方法中有一个modCount变量,记录修改次数,主要是用来实现fail-fast机制,可参考fail-fast机制
(4)在进行扩容的时候,首先将新的容量设为旧的容量的1.5倍,如果还不够,直接将传入的最小容量参数设为新的容量。
(5)做插入操作的时候,需要将插入位置的元素和它之后的元素往后移动,将插入位置空出,然后将新元素放入需要插入的位置,因此如果不在尾部插入的时候,需要移动大量的元素,插入效率并不高。
/** * 添加单个元素 */ public boolean add(E e) { // 设置最小容量为当前数组的实际大小+1,并确保数组的容量可以容纳 ensureCapacityInternal(size + 1); //在尾部添加新元素 elementData[size++] = e; return true; } /** * 在指定的位置添加新元素 */ public void add(int index, E element) { // 检查下标是否合法 rangeCheckForAdd(index); // 设置最小容量为当前数组的实际大小+1,并确保数组的容量可以容纳 ensureCapacityInternal(size + 1); System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } /** * 将集合内的元素添加到list中 */ public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; //确保容量 ensureCapacityInternal(size + numNew); // Increments modCount //将a数组复制到elementData中 System.arraycopy(a, 0, elementData, size, numNew); //更新大小 size += numNew; return numNew != 0; } /** * 在指定的位置添加集合的元素 */ public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; } /** * 将当前容量值设为实际元素的个数 */ public void trimToSize() { // 记录修改次数 modCount++; if (size < elementData.length) { //使用copyOf方法生成一个容量为size的数组,并将elementData的元素拷贝到新数组 elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } } /** * 确保容量足够扩展新元素,供外部调用 * @param minCapacity 最小容量 */ public void ensureCapacity(int minCapacity) { //最小可扩展大小,这一步是为了判断elementData是否等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // 如果不是DEFAULTCAPACITY_EMPTY_ELEMENTDATA,返回0 ? 0 // 如果等于返回默认容量大小,因为DEFAULTCAPACITY_EMPTY_ELEMENTDATA首次添加元素时,可以将数组扩展为默认容量的大小 : DEFAULT_CAPACITY; //如果最小容量大于最小可扩展大小 if (minCapacity > minExpand) { //根据最小容量是否大于当前数组的容量判断是否扩容 ensureExplicitCapacity(minCapacity); } } /** * 确保容量足够扩展新元素,私有方法,在内部调用 * @param minCapacity */ private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } //在该方法中判断是否进行扩容 ensureExplicitCapacity(minCapacity); } /** * 如果minCapacity大于当前数组的容量进行扩容 * @param minCapacity */ private void ensureExplicitCapacity(int minCapacity) { //记录修改次数 modCount++; // 如果需要扩展的容量大于当前数组的容量,进行扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); } /** * 最大数组容量值,Integer最大值-8 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * 扩容 */ private void grow(int minCapacity) { // 当然数组的容量 int oldCapacity = elementData.length; // 新的数组容量为旧的数组容量+旧的数组容量/2,也就是扩容到旧容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); // 如果新的数据的容量依旧小于最小容量 if (newCapacity - minCapacity < 0) //将最小容量的大小设为新数组的大小 newCapacity = minCapacity; //如果超过数组最大限度容量值 if (newCapacity - MAX_ARRAY_SIZE > 0) //调用超大容量扩容方法计算新的容量值 newCapacity = hugeCapacity(minCapacity); // 生成大小为newCapacity的数组,将旧数组的元素拷贝到新数组中 elementData = Arrays.copyOf(elementData, newCapacity); } /** * 超大容量的扩容大小计算 * @param minCapacity * @return */ private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); // 如果最小容量大于最大数组容量值,返回Integer最大值,否则返回最大数组容量值 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
4.查找和获取方法
(1)根据元素获取元素的下标的时候,元素为null和不为null的时候分别进行了处理,从中可以看出ArrayList是支持null值存在的
(2)如果存在重复的元素,返回第一个元素的位置
/** * 返回ArrayList的实际大小 */ public int size() { return size; } /** * 是否为空 */ public boolean isEmpty() { return size == 0; } /** * 判断是否包含指定的元素 */ public boolean contains(Object o) { return indexOf(o) >= 0; } /** * 获取指定元素的下标 */ public int indexOf(Object o) { if (o == null) { //遍历数组,寻找值为null的元素下标 for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { // 遍历数组,在数组中寻找指定元素o的下标 for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } //如果未找到返回-1 return -1; } /** * 反向查找,从数组的尾部向前查找 */ public int lastIndexOf(Object o) { if (o == null) { for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; } // Positional Access Operations @SuppressWarnings("unchecked") E elementData(int index) { return (E) elementData[index]; } /** * 根据下标获取元素 */ public E get(int index) { // 检查下标越界 rangeCheck(index); // 返回元素 return elementData(index); }
5.移除方法
(1)删除元素的时候实际上是将该元素之后的元素向前移动,将要删除的元素覆盖,如 1 2 3 4 5如果要删除4,将4之后的5向前移动一个位置变为1 2 3 5 5,然后将5原来的位置设为null,变为1 2 3 5 null。
(2)ArrayList删除时与插入一样,同样要移动大量的元素,因此效率也不高。
/** * 根据指定的位置移除元素 */ 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); //移除元素,将指定的位置的元素设为null elementData[--size] = null; // clear to let GC do its work //将删除的元素返回 return oldValue; } /** * 根据传入的对象删除,当对象在数组中存在的时候移除元素并返回true */ 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; } /* * 私有移除元素的方法,跳过了下标越界检查,并且不返回移除的元素 */ private void fastRemove(int index) { modCount++; //计算移动元素的数量 int numMoved = size - index - 1; if (numMoved > 0) // elementData从index+1位置后的numMoved个元素向前移,生成新的数组赋给elementData System.arraycopy(elementData, index+1, elementData, index, numMoved); //更新数组的实际大小 elementData[--size] = null; // clear to let GC do its work } /** * 清空 */ public void clear() { modCount++; //移除所有元素 for (int i = 0; i < size; i++) elementData[i] = null; //大小设为0 size = 0; } /** * 根据指定的下标范围删除元素 * */ protected void removeRange(int fromIndex, int toIndex) { //记录修改次数 modCount++; int numMoved = size - toIndex; //删除formIndex到toIndex的元素 System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); // 更新数组的实际大小 int newSize = size - (toIndex-fromIndex); // 删除操作是将toIndex之后的元素向前移,将需要删除的元素覆盖,这里将移动元素原来的位置设为null for (int i = newSize; i < size; i++) { elementData[i] = null; } size = newSize; } /** * 元素的下标越界检查 */ private void rangeCheck(int index) { // 如果下标大于或等于当前实际元素的大小,抛出下标越界异常 if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } /** * 添加元素时的下标越界检查 */ private void rangeCheckForAdd(int index) { // 如果下标大于当前实际元素的大小或者小于0,抛出异常 if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } /** * 越界提示信息 */ private String outOfBoundsMsg(int index) { return "Index: "+index+", Size: "+size; } /** * 删除所有出现在集合中的元素 */ public boolean removeAll(Collection<?> c) { //集合判空 Objects.requireNonNull(c); //批量删除 return batchRemove(c, false); } /** * 保留出现在集合中的元素,删除其他元素,与removeAll(Collection<?> c)刚好相反 */ public boolean retainAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, true); } /** * 批量删除 * @param c 集合 * @param complement removeAll(Collection<?> c)调用是为false,retainAll(Collection<?> c)调用是为true * @return */ private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; // r用来遍历数组,w用来记录删除元素后新的数组的大小 int r = 0, w = 0; boolean modified = false; try { //遍历数组 for (; r < size; r++) //对于removeAll方法来说,elementData[r]在集合中存在,此时条件true==false不成立,继续下一次循环 //如果elementData[r]在集合中不存在,false==false成立,在elementData的w位置处保留该元素,然后w自增 //retainAll与removeAll刚好相反,元素不在集合中出现时删除,在集合中出现时保留该元素 if (c.contains(elementData[r]) == complement) elementData[w++] = elementData[r]; } finally { // 如果数组未遍历完被中断的话,从数组的第r个位置开始,将size-r个元素向前移动r-w个位置 if (r != size) { System.arraycopy(elementData, r, elementData, w, size - r); //更新w的大小 w += size - r; } // w !=size表示集合中存在需要被删除的元素 if (w != size) { // 遍历数组,将w之和的元素清空 for (int i = w; i < size; i++) elementData[i] = null; //更新修改次数 modCount += size - w; //更新数组的大小 size = w; modified = true; } } return modified; }
6.更新元素的值
/** * 根据下标更新元素的值 */ public E set(int index, E element) { // 检查下标越界 rangeCheck(index); // 获取旧元素 E oldValue = elementData(index); // 设置新的值 elementData[index] = element; // 将旧的值返回 return oldValue; }
7.toArray、序列化和克隆方法
/** * 将ArrayList转为Object类型的数组 */ public Object[] toArray() { return Arrays.copyOf(elementData, size); } /** * 返回T[]类型的数组 */ @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { //如果a数组长度小于elementData的size if (a.length < size) // 将elementData拷贝到一个新数组中,并转为T[]返回 return (T[]) Arrays.copyOf(elementData, size, a.getClass()); //将elementData拷贝到a数组中 System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } /** * 序列化,将ArrayList写入到输出流 */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone() s.writeInt(size); // Write out all elements in the proper order. for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } /** * 反序列化 */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { elementData = EMPTY_ELEMENTDATA; // Read in size, and any hidden stuff s.defaultReadObject(); // Read in capacity s.readInt(); // ignored if (size > 0) { // be like clone(), allocate array based upon size not capacity ensureCapacityInternal(size); Object[] a = elementData; // Read in all elements in the proper order. for (int i=0; i<size; i++) { a[i] = s.readObject(); } } } /** * 克隆方法,返回的是浅拷贝 */ public Object clone() { try { java.util.ArrayList<?> v = (java.util.ArrayList<?>) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } }