动态数组(Dynamic Array)

1,393 阅读2分钟

一、动态数组产生的原因

在许多编程语言中数组是无法动态修改容量,所以需要数组的长度能够进行动态的变化, 在java JDK内置了动态数组类:java.util.ArrayList

二、动态数组的主要接口实现

1、构造方法

如果创建数组的空间小于默认空间,则会以默认空间创建数组

public class ArrayList<E> {
    private int size;
    private E[] elements;
    
    private static final int CAPACITY_DEFAULT = 10;

    public ArrayList(int capacity) {
        capacity = (capacity < CAPACITY_DEFAULT) ? CAPACITY_DEFAULT : capacity;
        elements = (E[]) new Object[capacity];
    }

    public ArrayList() {
        this(CAPACITY_DEFAULT);
    }
}

2、添加元素

添加元素可以分为两种情况:

  • 在数组末尾添加元素即插入到数组的尾部size处
public void add(E element) {
    add(size, element); 
}
  • 在数组中间插入元素
public void add(int index, E element) {
    if (index < 0 || index > size) {
        throw new IndexOutOfBoundsException("index:" + index + ", Size:" + size);
    }
    // 检测是否需要扩容
    ensureCapacity(size + 1);

    for (int i = size; i > index; i--) {
        elements[i] = elements[i - 1];
    }
    elements[index] = element;
    size++;
}

2.1、数组动态扩容

实现策略:

  • 在加入元素之前首先进行判断是否需要扩容
  • 如果需要创建一个新的数组,新数组的大小官方设置是原数组的1.5倍
  • 将原数组中的元素存到新的数组中
private void ensureCapacity(int capacity) {
    // 获得当前数组的容量
    int oldCapacity = elements.length;
    if (oldCapacity > capacity) return;
    // 扩容的大小使原来的1.5倍,位运算符的运算较快
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    E[] newElements = (E[]) new Object[newCapacity];
    for (int i = 0; i < size; i++) {
        newElements[i] = elements[i];
    }
    // 引用新数组
    elements = newElements;
}

3、删除元素

指定元素的下标,将后面的元素逐个向前移,覆盖掉前面的元素

public E remove(int index) {
    if (index < 0 || index >= size) {
        throw new IndexOutOfBoundsException("index:" + index + ", Size:" + size);
    }
    E element = elements[index];
    for (int i = index; i < size; i++) {
        elements[i] = elements[i + 1];
    }
    elements[--size] = null;
    // 是否需要缩容
    trim();
    return element;
}

3.1、动态缩容

为了避免内存的浪费,当数组中的元素小于一定数量后进行缩容

private void trim(){
    int capacity = elements.length;
    if (size >= capacity >> 1 || capacity < CAPACITY_DEFAULT) return;
    // 新的容量为原来容量的一半
    int newCapacity = capacity >> 1;
    E[] newElements = (E[]) new Object[newCapacity];
    for (int i = 0; i < size; i++){
        newElements[i] = elements[i];
    }
    elements = newElements;
}

三、动态数组的复杂度

增删元素的最好时间复杂度是O(1),最坏是O(n),平均时间复杂度是O(n)
改查的时间辅助度是O(1)级别,因为是通过数组的索引来实现