ArrayList是最常用的数组,这里我们分析一下源码:
ArrayList继承了AbstractList<E> ,并且实现了List<E>, RandomAccess, Cloneable, java.io.Serializable
里面有几个比较重要的成员变量:
序列化Id
private static final long serialVersionUID = 8683452581122892189L;
默认的初始化容量10:
private static final int DEFAULT_CAPACITY = 10;
用于空实例的共享空数组实例:
private static final Object[] EMPTY_ELEMENTDATA = {};
用于空实例的共享空数组实例,这个跟EMPTY_ELEMENTDATA的区别是在于当第一个元素被添加以后怎么知道需要多少扩容
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
数组缓冲区中的数组的元素存储。ArrayList的容量是这个array buffer的的长度
transient Object[] elementData;
ArrayList的长度
private int size;
这是一个常量,用来限制数组的最大长度,Integer.MAX_VALUE = 2的31次方-1
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
下面看一下ArrayList的几个构造函数:
这个是指定初始大小的构造函数
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];//如果初始值>0则初始化数组
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;//如果初始值为0,则将孔数组赋给这个数组
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
这个是默认的构造函数
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;会把默认的空数组赋值给这个数组
}
还有一个构造方法,传入的参数是一个collection对象
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();//把collection转为一个Array
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)//是否成功转化为Object类型数组
elementData = Arrays.copyOf(elementData, size, Object[].class);//转化失败就进行复制
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
下面是分析里面具体的每个方法:
把 ArrayList实例的容量装饰成这个list当前的大小。一个应用可以使用这个方法来最小化ArrayList实例的产生
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
增加ArrayList实例的容量,如果有必要,要确保这个能保证当前所有元素最小的容量
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)//先判断一下数组是不是为空
if (minCapacity > minExpand) { 如果最小容量大于默认容量,就要开始扩容了
ensureExplicitCapacity(minCapacity);
}
}
进行扩容初始化,这是一个私有方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//找到最小的初始容量大小
}
ensureExplicitCapacity(minCapacity);
}
这也是一个私有方法,开始进行扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)//判断最小的容量是不是> 数组的长度
grow(minCapacity);//扩容的具体实现
}
这是个私有方法,这个是扩容的具体实现
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //获取当前数组的大小
int newCapacity = oldCapacity + (oldCapacity >> 1); //新数组的容量算法为:当前数组大小 + (当前数组大小 / 2),相当于原来的1.5倍大小
if (newCapacity - minCapacity < 0)//如果新的容量大小比最小的容量还小,就用最小扩容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)//如果新的容量大小比数组规定的最大容量还大,就调用hugeCapacity来算容量
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);//进行扩容
}
这个也是一个私有方法,用来算出当新的数组容量比Java规定的最大数组容量还大的时候,要设置多少作为最大容量
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? //判断最小容量是不是已经超过了Java规定的最大数组容量
Integer.MAX_VALUE : 超过了就用Integer最大值 2的31次方-1
MAX_ARRAY_SIZE; 没有就用数组最大容量 Integer.MAX_VALUE - 8
}
返回list里面元素的个数
public int size() {
return size;
}
判断list是不是为空,通过判断szie是不是为0
public boolean isEmpty() {
return size == 0;
}
判断是不是传入的元素包含在了当前list里面,具体实现是在indexOf方法
public boolean contains(Object o) {
return indexOf(o) >= 0;//这个意思就是至少包含一次,通过查看indexof()的代码,返回值是数组下标,而数组下标是从0开始的,所以这里写>= 0
}
这个方法是把传入的Object对象跟list里面的值进行比较,返回的是符合要求的值的数组下标而不是个数
public int indexOf(Object o) {
if (o == null) {//这段代码刚开始没有看懂,后来又看了看,原来判断当前传入的对象如果为空,
for (int i = 0; i < size; i++)//并且list里面也有空对象,那么也是要找到,并说明存在为null的
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)//这个就是通过用Object的equals方法来做比较,当出现有符合要求的值就返回这个值的数组下标
if (o.equals(elementData[i]))
return i;
}
return -1;//没有符合条件的就返回-1
}
这个是ArrayList的一个克隆方法,返回的一个浅克隆的ArrayList实例,但是ArrayList里面的元素是没有被克隆的
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();这里面是调用了Object的clone()方法
v.elementData = Arrays.copyOf(elementData, size);用Arrays的copyOf方法来把元素都放进新的实例中
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
返回一个数组包含了所有当前list的元素,按照当前的数组序列,实际是用Arrays的copyOf方法来实现的
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
返回一个数组包含了所有当前list的所有元素,并且元素类型是指定的元素类型
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size) //如果传入的数组长度大于当前的的数组长度,则返回空
a[size] = null;
return a;
}
根据数组下标返回元素
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
根据数组下标找到元素
public E get(int index) {
rangeCheck(index);//数组越界检查
return elementData(index);//通过调用elementData来找到相应元素
}
将传入的参数元素添加到指定的下标渣饼返回这个元素
public E set(int index, E element) {
rangeCheck(index);//数组越界检查
E oldValue = elementData(index);//先找到当前元素
elementData[index] = element;//把传入的元素设置到指定的位置
return oldValue;
}
添加一个元素到到当前list里面,如果添加成功则返回true
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!//新进行扩容判断
elementData[size++] = e;//直接在当前list的后面进行添加
return true;
}
将传入的元素添加在指定的数组下标地址
public void add(int index, E element) {
rangeCheckForAdd(index);//判断数组下标是不是越界
ensureCapacityInternal(size + 1); // Increments modCount!!//判断是不是需要扩容
System.arraycopy(elementData, index, elementData, index + 1,
size - index);//这个方法很有意思,通过调用这个方法将要设置的数组下标空出来,然后之前的所有它后面的元素向后移动一个位置。这个就是经常提到的为什么数组在插入的时候时间复杂度很大,因为它后面的元素都要向后移动一个位置
elementData[index] = element;//在list里面将传入的元素赋值给指定的数组下标位置
size++;
}
//将指定的数组下标从list中删除
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; // clear to let GC do its work
return oldValue;//并返回这个元素
}
//将传入的Object对象从数组中移除,移除成功则返回true
public boolean remove(Object o) {
if (o == null) {//这里为什么为null还要处理,因为list里面还有为空的对象
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);//调用fastRemove进行删除
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {//通过遍历找到传入的Object然后进行删除
fastRemove(index);
return true;
}
}
return false;
}
这是一个私有的方法,用来快速删除元素,这个方法跟remove是一样的,只是这个方法没有检测数组是不是越界了,并且没有返回值
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
这个方法就是将数组清空
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
将传入的collection全部添加到当前数组
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();//通过这个方法将collection变成一个数组
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount//通过当前数组的大小跟传入的数组大小进行扩容判断
System.arraycopy(a, 0, elementData, size, numNew);//通过这个方法把传入的collection都加入到当前数组
size += numNew;
return numNew != 0;
}
这个方法是将传入的数组放在当前list的指定位置
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);//先将当前数组在index以及以后的元素往后移动
System.arraycopy(a, 0, elementData, index, numNew);//将传入的数组从指定的位置开始插入
size += numNew;
return numNew != 0;
}
这个方法是将指定数组范围内的元素删除
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);//先将需要删除的元素范围的位置给移除掉
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
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));
}
数组越界检查,这个检查是在add的时候用
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
这个是构造数组越界的时候的异常语言
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
这个方法是将所有包含在collection的元素从当前数组中移除
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);//调用Object的判断是不是空指针的方法
return batchRemove(c, false);//调用这个方法来删除
}
这个方法正好相反,是将所有没有包含在collection的元素从当前数组中移除
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
这个方法就是上面两个方法的具体实现,其中complement为true代表删除所有不包含在传入的数组里面的元素,为false时代表删除所有在传入的数组中的元素
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)//这个逻辑就是根据传入的boolean值来判断是怎么删除的
elementData[w++] = elementData[r];
} finally {//将移除后的元素进行删除
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
将一个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();//调用ObjectOutputStream的defaultWriteObject,这个方法是用来将一个类的非静态和非transient区域转化为流
// 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();
}
}
从stream反序列化成一个ArrayList
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();
}
}
}
从传入的位置开始返回一个iterator list包含了这个位置以后的所有元素
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
返回一个包含了所有元素的iterator list
public ListIterator<E> listIterator() {
return new ListItr(0);
}
这个方法也是返回一个包含了所有元素的iterator list,只是实现方式不一样
public Iterator<E> iterator() {
return new Itr();
}
往下分析发现了一些内部类以及一些不常用的方法,这里就不一一列举了,有兴趣可以自己看一下源码
这个主要说一下这个sort方法,这个经常被用到:ArrayList的sort是重写了父类List的sort排序,而在List里面调用了Arrays的sort排序算法,如果大家想要实现自己的逻辑排序算法的话,要调用sort方法并且新写一个Comparator重写compare方法,compare方法的返回值,代表了两个数的比较结果
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
下面是一个简单的运用sort实现Student类按照年龄进行排序的(Student类就不写了,只是里面有age这个成员变量)
List<Student> datas = new ArrayList<Student>();
datas.sort(new Comparator<Student>(
) {
@Override
public int compare(Student o1, Student o2) {
// return o1.getAge() - o2.getAge(); 升序
return o2.getAge() - o1.getAge(); //降序
}
});