java 数组和集合本质区别

406 阅读3分钟

Java中的数组和集合(如ArrayList、HashSet等)有许多本质区别。要理解这些区别,我们需要从它们的特性、使用方式和底层实现上进行对比。

核心差异

  1. 定义与类型

    • 数组是一种固定长度的数据结构,存储相同类型的数据。
    • 集合(如List、Set、Map等)是可变长度的数据结构,可以动态调整大小,并且提供了更多的操作方法。
  2. 内存分配

    • 数组在创建时就确定了大小,内存空间连续分配,无法动态调整大小。
    • 集合则可以动态调整大小。例如,ArrayList 在内部通过动态数组来实现,会根据需要扩展或收缩。
  3. 性能

    • 数组访问元素非常快,因为可以直接通过索引访问。
    • 集合中不同类型的实现有不同的性能特点。例如,ArrayList 的随机访问较快,但增加或删除元素(尤其是在中间位置)速度较慢。而 LinkedList 增加或删除较快,但随机访问较慢。
  4. 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)。

总结

  • 数组:固定长度,连续内存分配,访问速度快,但功能有限。
  • 集合:可变长度,提供丰富的操作接口,灵活但复杂度高。