ArrayList通过Object[] elementData保存数据
初始化
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// defend against c.toArray (incorrectly) not returning Object[]
// (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
通过Collection初始化时,内部实际调用Arrays.copyOf
@HotSpotIntrinsicCandidate
public static <T,U> 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;
}
如果类型时Object,则直接传教Object[],否则通过Array.newInstance创建该类型的数组。最后通过System.arraycopy直接调用Cpu指令复制。
可以看出实际把所有的元素都转换成Object类型。这是因为有一个bug
Arrays.asList(x).toArray().getClass() should be Object[].class
该方法有@HotSpotIntrinsicCandidate注解,说明可能被指令直接替换优化
容量
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//每次扩容在原来的基础上扩大一半
//要考虑变成负数的情况
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
这个函数咋一看有问题,因为会存在扩容后为负数,则直接返回minCapacity的情况。实际上调用该方法的地方都已经做了判断并且确实minCapacity比实际值大。这样里面判断minCapacity<0的Overflow判断才确实有用。例如
public void ensureCapacity(int minCapacity) {
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
&& minCapacity <= DEFAULT_CAPACITY)) {
modCount++;
grow(minCapacity);
}
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private Object[] grow() {
return grow(size + 1);
}
remove相关方法通过System.arraycopy方法移动删除的元素
//删除所有参数集合中的值
public boolean removeAll(Collection<?> c) {
return batchRemove(c, false, 0, size);
}
//保留所有集合中的元素
public boolean retainAll(Collection<?> c) {
return batchRemove(c, true, 0, size);
}
boolean batchRemove(Collection<?> c, boolean complement,
final int from, final int end) {
Objects.requireNonNull(c);
final Object[] es = elementData;
int r;
// Optimize for initial run of survivors
for (r = from;; r++) {
if (r == end)
return false;
if (c.contains(es[r]) != complement)
break;
}
int w = r++;
try {
for (Object e; r < end; r++)
if (c.contains(e = es[r]) == complement)
es[w++] = e;
} catch (Throwable ex) {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
System.arraycopy(es, r, es, w, end - r);
w += end - r;
throw ex;
} finally {
modCount += end - w;
shiftTailOverGap(es, w, end);
}
return true;
}
这个batchRemove笔记有意思。complement判断是删是留。假设删除(false).
1.第一个循环找到第一个需要删除的元素,下表记作r.
2.w=r++,这样w指向原来的元素,r指向下一个元素。
3.下一个循环找到不需要删除的元素,es[w++]=e则将不符合条件的元素复制到w处,w指向下一个元素。这样w不断的替换为不需要删除的元素。
4.最后把剩下的元素删除。
这样比申请一个新的数组,把待保留的元素保留到新数组要节省空间。
剩下的就是公共的比如iterator,spliter等一些公共方法。