何鑫个人博客

Java中的动态数组——ArrayList探秘(一)

  • 2021-03-22 16:55:39
  • 技术
  • 193

ArrayList概述

ArrayList是一种很常见的数据结构,在实际工作中也非常常用。
我们可以从官方介绍中总结出它的特点。

  • 这是一种实现了List接口的动态数组(基于数组实现,但是容量可变化)
  • 未加锁(线程不安全)
  • 可以容纳所有元素,包括null(基本类型要使用包装类,底层是Object数组)
  • 有初始容量(即初始数组的大小,默认为10),根据需要自动增长

属性分析

  • 常量
// 定义初始容量为10
private static final int DEFAULT_CAPACITY = 10;

// 当使用带参构造并传入的初始大小为0时创建ArrayList对象时持有的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};

// 当使用无参构造创建ArrayList对象时默认持有的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  • 变量
// ArrayList持有用于存储数据的数组
transient Object[] elementData;

// ArrayList的长度
private int size;
  • 构造函数分析
    构造函数可以帮助我们了解我们在创建对象的时候发生了什么。
// 无参构造
public ArrayList() {
    // 把持有的数组指向默认定义的空数组
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

这是我们最常用的创建ArrayList对象的方式。我们可以看到以这种方式创建对象时仅仅把持有的数组变量指向了DEFAULTCAPACITY_EMPTY_ELEMENTDATA,一个默认的空数组。

// 有参构造-1
public ArrayList(int initialCapacity) {
    // 校验传入的用于指定默认容量的int值
    if (initialCapacity > 0) {
        // 以传入的int值创建一个Object数组并引用它
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        // 如果传入0,则引用已定义的空数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
        initialCapacity);
    }
}

// 有参构造-2
public ArrayList(Collection<? extends E> c) {
    // 把传入的集合转变为数组
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // 校验是否Object数组
        if (elementData.getClass() != Object[].class)
            // 不是则复制并转变为Object数组并引用它
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // 数组长度为0则引用空数组
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

有参构造1让我们可以使用一个我们规定的大小来初始化ArrayList的长度,如果传了0则默认引用一个已定义的空数组,大于0则创建一个指定大小的数组并引用它,其他不合法的值则抛出错误。
有参构造2接收一个已有的集合类型,然后把该集合转变为一个数组并持有它。如果该数组大小为0,则持有一个已经定义的空数组,否则检查该数组是否为Object数组,不是就使用Arrays.copyOf方法转为Object数组并重新引用它。