Java中的数组和集合(如ArrayList、HashSet等)有许多本质区别。要理解这些区别,我们需要从它们的特性、使用方式和底层实现上进行对比。
核心差异
-
定义与类型:
- 数组是一种固定长度的数据结构,存储相同类型的数据。
- 集合(如List、Set、Map等)是可变长度的数据结构,可以动态调整大小,并且提供了更多的操作方法。
-
内存分配:
- 数组在创建时就确定了大小,内存空间连续分配,无法动态调整大小。
- 集合则可以动态调整大小。例如,
ArrayList在内部通过动态数组来实现,会根据需要扩展或收缩。
-
性能:
- 数组访问元素非常快,因为可以直接通过索引访问。
- 集合中不同类型的实现有不同的性能特点。例如,
ArrayList的随机访问较快,但增加或删除元素(尤其是在中间位置)速度较慢。而LinkedList增加或删除较快,但随机访问较慢。
-
API 支持:
- 数组只有少量的基本操作(例如,通过索引获取/设置元素)。
- 集合框架提供了丰富的API,支持添加、删除、查找、排序等复杂操作。
源码分析
数组
数组作为Java语言的一部分,其底层实现由JVM处理。以下是一个简单的数组示例:
java复制代码
int[] arr = new int[10];
arr[0] = 1;
上面的代码表示:
- 创建一个长度为10的整数数组。
- 将第一个元素设为1。
数组在内存中连续存储,每个元素的地址可以通过基地址加偏移量计算得出。因此,访问数组元素时速度非常快。
ArrayList(集合)
ArrayList 是集合框架的一部分,底层采用动态数组实现。我们来看一下 ArrayList 的一些核心源码片段:
java复制代码
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // 存储元素的数组
private int size;
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);
}
}
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 确保容量足够
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
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);
}
}
上面的代码展示了 ArrayList 的一些核心方法:
elementData是存储元素的动态数组。add方法会在必要时调用ensureCapacityInternal确保数组有足够的容量。grow方法在容量不足时将数组扩展到原来的1.5倍(右移一位相当于除以2)。
总结
- 数组:固定长度,连续内存分配,访问速度快,但功能有限。
- 集合:可变长度,提供丰富的操作接口,灵活但复杂度高。