ArrayList(jdk1.6)
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData;
内部用Object[]数组存储数据
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @exception IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this(10);
}
无参构造函数,默认创建容量为10的数组
含参构造函数,如容量为负数,则抛出异常
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
添加元素之前会先判断添加此元素时容量会不会不够,如不够则需要进行扩容
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity; // minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
扩容方式后的新容量为(oldCapacity * 3)/2 + 1,如计算之后的新容量还是小于minCapacity(有可能添加的是一个集合),那么新容量就等于minCapacity。
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
在指定位置添加元素,扩容方式和之前一样,但会想判断边界index > size || index < 0则抛出异常(index=size或index=0代表插入尾和头),接下来会把从index开始往后的元素拷贝到从index+1往后的位置 ,再把新增的元素加到index处的位置。
插播一下Arrays.copyOf和System.arraycopy的区别,Arrays.copyOf内部实现其实就是调用System.arraycopy,只不过加了一层保护即取新长度和源数组长度的最小值,防止数据溢出。
public static T[] copyOf(U[] original, int newLength, Class extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
public void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (size < oldCapacity) {
elementData = Arrays.copyOf(elementData, size);
}
}
把数组容量缩减至和元素实际数量相同
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
判断是否包含某一元素
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;
}
元素可以为null,实际就是遍历数组中的元素,看有无相等
public E get(int index) {
RangeCheck(index);
return (E) elementData[index];
}
获取元素,首先会判断索引值是否符合要求
private void RangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
}
public E set(int index, E element) {
RangeCheck(index);
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
设置某一索引处的值为一个新值,也会判断所以是否符合要求,即不能用此来在队尾添加元素,并返回修改之前的值。
public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
return oldValue;
}
删除索引处的元素,其会导致索引后的元素全部向前移动,移动的数量为size-index-1,然后把从索引后一位开始的全部数据拷贝到从索引处开始往后的位置,并把移动后最后多余的位置置为空,方便让GC回收。
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;
}
删除某一元素,可以为null,就是遍历处该元素所在的索引位置,然后调用fastRemove,其实现原理和上面删除索引处的元素实现大致相同。
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; // Let gc do its work
}
public void clear() {
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
清除即遍历所有元素置为空,以便GC回收。
ArrayList(jdk1.7)
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
*/
private transient Object[] elementData;
jdk1,7中的ArrayList实现中多出了默认容量和一个空数组
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
jdk1.7中的无参构造函数不是默认创造容量为10的数组,而是用一个空数组代替。含参的构造函数与jdk1.6中一样。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
jdk1.7中的增加函数也同样会在增加元素时进行扩容判断,首先会判断是不是空数组,如果是那么minCapacity会取他和DEFAULT_CAPACITY中较大的一个。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
如minCapcity大于当前数组的容量那么就进行扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
新容量为旧容量的1.5倍,如扩充后的新容量的值还小于minCapacity,那么新容量就等于minCapcaity,如果新容量的值比MAX_ARRAY_SIZE(整形最大值-8)还要大,那么新容量取整形的最大值。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != EMPTY_ELEMENTDATA)
// any size if real element table
? 0
// larger than default for empty table. It's already supposed to be
// at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
jdk1.7中的ensureCapacity不在是用来在添加时进行扩容的函数,而是独立的扩容函数,如果是空数组那么会与DEFAULT_CAPACITY比较在决定是否可以扩容,如果不是空数组,那么直接进行扩容判断。
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;
size++;
}
与jdk1.6中思路类似,只是扩容函数不同。
trimToSize,contains,get,set,remove,clear与jdk1.6中完全一样
ArrayList(jdk1.8)
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
相比于jdk1.7,又多了一个DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空数组。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
无参构造函数,赋值为新增的DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组,含参构造函数,如果等于0,则赋值为EMPTY_ELEMENTDATA的空数组
其他函数与jdk1.7基本完全相同,唯一区别的地方在于add扩容时有些差别
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;
size++;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
多了一步计算,即如果是用无参构造函数构造,那么就取DEFAULT_CAPACITY与minCapacity中较大的值作为基础值,后面的处理与jdk1.7中一样。
jdk1.7和jdk1.8相比于jdk1.6的优势在于避免过早的开辟数组空间,所以采用空数组进行复制,而jdk1.8相对于jdk1.7又改进了,在含参构造函数中取值为0是,新建容量为0的数组。