解析ArrayList
ArrayList剖析
数据结构概念
ArrayList的数据结构的本质是数组列表,是一段连续的空间,它支持对元素的快速随机访问(Random Access),即使用了索引遍历,通过ArrayList首位的地址+偏移量(即下标)来查找元素。
非线程安全的。
源码层次
- 继承抽象类与接口
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{......}
- 遍历效率问题
实现了RandomAccess接口,意味着索引遍历(for循环遍历)快于迭代器遍历
RandomAccess标志接口描述如下:
/**
* <pre>
* for (int i=0, n=list.size(); i < n; i++)
* list.get(i);
* </pre>
* runs faster than this loop:
* <pre>
* for (Iterator i=list.iterator(); i.hasNext(); )
* i.next();
* </pre>
*
* <p>This interface is a member of the
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
*
* @since 1.4
*/
public interface RandomAccess {
}
- 构造
ArrayList有两个构造方法:
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 = {};
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;
}
可见,无参构造实际是建立了一个空数组,长度为0;有参构造传入参数即需要创建的数组大小。
-
扩容机制
在ArrayList中添加元素时使用add方法
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }可见,其第一步是增加长度ensureCapacityInternal(size + 1),看看ensureCapacityInternal方法
private static final int DEFAULT_CAPACITY = 10; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 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; } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
由
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
} return minCapacity;
可见如果在添加元素时,数组为空数组,则初始化一个长度为10的数组,否则就 +1(因为minCapacity = size + 1)
if (minCapacity - elementData.length > 0)
grow(minCapacity);
通过比较,当原来的数组长度小于所需长度,则调用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);
}
注意,此方法使用的是int newCapacity = oldCapacity + (oldCapacity >> 1);
所以扩容是加上向右移动一次的大小,即每次扩容是原来大小的1.5倍
扩容后要确定新数组长度是否合理
下一步elementData = Arrays.copyOf(elementData, newCapacity);
将老数组的元素copy到新的已经扩容的数组中。
有错误请联系笔者或在评论区指出,感谢

浙公网安备 33010602011771号