/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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;
}
集合构造函数大概有两种,有参和无参。
不管是有参还是无参,都会把new出来的数组赋值给elementData,这是一个重要的对象。
有参的初始化,initialCapacity这个参数大于0,则直接new 一个Obj数组,如果等于0则返回一个空数组。
/**
* 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 = {};
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return true (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
调用ensurecapacityInternal方法(),把入参放到数组相对应的索引空间内,返回true。
size是int类型,没有赋值则默认为0
ensurecapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
调用calculateCapacity() 和 ensureExplicitCapacity()
calculateCapacity()
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
这个时候会用到构造函数时初始化的elementData对象,去判断它和
DEFAULTCAPACITY_EMPTY_ELEMENTDATA是不是同一个内存地址,这个是无参的空数组,如果是有参的构造函数则不相等。
相等则返回size+1和DEFAULT_CAPACITY两者较大的,
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
显然是返回默认值10。
不相等则返回我们原本的空间大小,也就是size+1。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
判断minCapacity (上一个方法的返回值)和elementData数组的长度
如果你有参定义集合的长度,那你的minCapacity有可能小于elementData,也有可能大于elementData。
如果你是无参构造的集合,就一定大于elementData。
这里关键点是大于的情况,因为当你大于0的时候会调用grow()方法,也就是我们所说的集合扩容。
grow()
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);
}
oldCapacity获取你当前集合的内存长度。
newCapacity等于你当前长度乘以1.5,这也就是J8的Array的集合的默认扩容机制
如果扩容后的长度任小于你当前的集合长度最小值。
则扩容长度改为你当前的集合长度最小值。(说到这里可能会混淆,因为这里面实际上有三个概念对象,第一个是你当前集合的实际长度,第二个是经过上面方法计算后的集合预期最小长度,第三个是理论扩容长度)
(不管如何,理论扩容长度一定要大于或者等于预期最小长度。举个例子,你初始化为0,你扩容后的长度还是0,但是你经过上面默认值赋予的10,你的预期最小长度应该是10,所以扩容长度要等于10。)
如果你扩容长度大于最大数据长度(上界),调用hugeCapacity()
hugeCapacity()
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
(你会得到整型最大的值,也就是你的集合长度不能超过整型的最大值)
最后copy你的对象。旧的elementData数组,复制到新的数组对象中,且新数组的大小为你的扩容长度。
Arrays.copyOf()
public static T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static T[] copyOf(U[] original, int newLength, Class extends T[]> newType) {
@SuppressWarnings("unchecked")
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;
}
如果你旧对象的类型也是objclass则,返回一个obj的对象(扩容长度),否则返回一个你的类型的getComponentType()类型的数组实例回去(不做多说明,因为集合的数组就是obj)。
最后进行内存数据复制,把旧的elementData复制到我们新的obj数组上,从索引0开始。
返回新的obj数组。
elementData = Arrays.copyOf(elementData, newCapacity);
然后又赋值给elementData的对象引用,这就完成了一次集合数组的扩容。
总结
无参集合在第一次add操作后new了一个默认为10的长度的数组
size是集合内存在的元素数量。
elementData才是决定该集合内存长度的对象。
扩容机制乘以1.5,最大就是整型的最大值
ArrayList集合的扩容实际上进行了把旧数组一个一个复制到新数组的地址上的操作(影响性能关键点),所以在添加操作导致扩容行为产生时,效率很低。
底层是数组,内存地址连续,所以根据索引,查询的效率很高。
(当然还有删除操作,但是我累了,有缘再写)