Java中的ArrayList是一种基于动态数组实现的数据结构,非常常用。相对于传统的数组,ArrayList具有更为灵活的可扩展性和易操作性。那么,在Java编程中,如何理解ArrayList的源码结构呢?接下来我将进行一些简单的分析说明。
ArrayList源码结构
概述
ArrayList类定义了Java中的动态数组,在下面的代码中可以看到其“add”、“remove”等常用方法声明:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
...
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
...
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // 可选清null操作,帮助GC
return oldValue;
}
...
}
成员变量
ArrayList源码中的一个重要部分就是元素数组elementData
,下面给出其定义:
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
...
其中,默认容量是10个元素,可通过构造方法实现动态扩容。此外,这里还定义了两个空数组常量,分别用于空参构造函数和初次扩容用。
构造方法
ArrayList中提供了多个构造方法,下面介绍两种常用方式:
// 使用空参构造器默认生成空数组
public ArrayList() {
this.elementData = 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);
}
}
注意,空参构造函数的elementData数组是通过DEFAULTCAPACITY_EMPTY_ELEMENTDATA
定义的空数组常量生成的。而在指定初始化容量的构造函数中,如果参数initialCapacity小于等于0,则抛出异常。
扩容策略
在对ArrayList进行插入操作时,若插入元素个数超出数组容量,就需要对数组进行扩容。下面是ArrayList的扩容逻辑:
private void grow(int minCapacity) {
// 当前数组容量
int oldCapacity = elementData.length;
// 新的容量至少是旧容量的1.5倍
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);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 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;
}
如上代码所示,当ArrayList进行扩容时,容量大小至少为原来的1.5倍,如果扩容后容量依然不足,则直接扩容到目标值。若扩容后的容量超过了数组最大长度,则调用hugeCapacity
方法进行溢出检查。最后,通过Arrays.copyOf
方法将旧数组复制到新数组中,完成扩容操作。
示例说明
下面给出两个简单的示例说明ArrayList代码的应用:
示例1:使用ArrayList存储字符串
创建一个长度为10的ArrayList数组,然后向其中添加字符串元素:
// 新建一个长度为10的ArrayList对象
ArrayList<String> arrayList = new ArrayList<>(10);
// 向元素数组插入字符串
arrayList.add("hello");
arrayList.add("world");
arrayList.add("!");
示例2:转换数组类型(复制)
将字符串数组转换为ArrayList类型,使用Arrays.asList
方法(此方法不能修改元素):
String[] arr = new String[]{"red", "green", "blue"};
ArrayList<String> arrayList = new ArrayList<>(Arrays.asList(arr));
这个示例中,使用Arrays.asList
方法将字符串数组arr
复制到arrayList
对象中。注意,此方法返回的ArrayList对象类型不可修改 (fixed-size),但是它除了不可修改外,其他List特性都是正常的。如果不使用这种方式,可能需要逐个遍历数组中的元素,调用add
方法逐个添加。
总结
Java中的ArrayList是一种基于动态数组实现的数据结构,拥有灵活的可扩展性和易操作性。通过分析ArrayList的源码结构,我们可以更深入地理解其内部实现机制,提高我们在实际编程过程中的处理效率。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java编程中ArrayList源码分析 - Python技术站