Java中Arraylist的函数实现原理详解
ArrayList是Java语言中的一种动态数组,在实现原理上有一些非常重要的细节需要注意。本文将从数组存储原理、扩容机制、迭代器实现、线程安全性等方面进行详细解析。
1. 数组存储原理
ArrayList是通过数组对数据进行存储的,用数组存储数据的好处是可以通过下标访问元素,因此对于随机访问的操作效率比较高。在ArrayList中,数组由elementData字段进行维护,其初始大小为10。
2. 扩容机制
当数组中的元素个数达到数组容量时,需要进行扩容。在ArrayList中,扩容的机制是在添加元素时进行的。当添加一个元素时,首先判断数组是否已满。如果数组已满,则调用grow方法进行扩容。
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
在grow方法中,首先获取当前数组的容量 oldCapacity,然后通过 oldCapacity >> 1 计算出扩容后的新容量 newCapacity。其中,>> 1 表示将 oldCapacity 右移一位,相当于 oldCapacity 除以 2 的结果。如果新容量小于添加元素所需的容量 minCapacity,则使用 minCapacity 值作为新容量。另外,如果新容量超过了数组的最大容量 MAX_ARRAY_SIZE,则调用 hugeCapacity 方法进行处理。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
在 hugeCapacity 方法中,首先判断 minCapacity 是否小于0,如果小于0则抛出 OutOfMemoryError。如果 minCapacity 大于 MAX_ARRAY_SIZE,则将容量设置为 Integer.MAX_VALUE(即数组长度的最大值),否则设为 MAX_ARRAY_SIZE。
最后,在扩容完成后,将原数组中的元素复制到新数组中。
3. 迭代器实现
在对ArrayList进行迭代时,Java提供了Iterator接口供我们使用。其中,Arraylist的迭代器实现是通过内部类Itr来实现的,代码如下:
private class Itr implements Iterator<E> {
int cursor; // 下一个元素的下标
int lastRet = -1; // 最后一个返回的元素的下标
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
// 当数组在迭代过程中被修改时,抛出ConcurrentModificationException异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
在Itr中,包含了当前元素的下标 cursor、上一个元素的下标 lastRet 和集合的修改次数 modCount 等字段。在hasNext方法中,判断 cursor 是否等于 size,如果相等就代表已经迭代到了最后一个元素,没有其它元素可以迭代,因此返回false。在next方法中,首先调用checkForComodification方法判断是否发生过ConcurrentModificationException异常,然后获取当前元素的下标 i,如果 i 大于等于 size,则抛出NoSuchElementException异常,如果 i 大于等于数组长度 elementData.length,则说明在迭代过程中发生了ConcurrentModificationException异常,因为数组的容量已经发生了变化。最后,将 cursor 的值加1,返回当前元素值。在remove方法中,首先检查lastRet的值,如果小于0则抛出IllegalStateException异常;然后调用checkForComodification方法检查是否发生过ConcurrentModificationException异常;最后,调用ArrayList的remove方法删除元素。
4. 线程安全性
ArrayList不是线程安全的,不建议在多线程环境下直接使用。如果需要在多线程环境下使用,需要采取一些线程安全措施,如使用Collections.synchronizedList方法将ArrayList转换为线程安全的List,或使用CopyOnWriteArrayList类代替ArrayList。
综上所述,ArrayList通过数组对数据进行存储,通过扩容机制进行容量管理,迭代器实现对数组元素进行遍历,因为非线程安全,所以在多线程环境下需要采取线程安全措施。对于ArrayList的理解和应用可以帮助我们更好的掌握Java语法和数据结构。
