数据结构:线性表之动态数组

947 阅读4分钟

概述

  1. 线性表是具有N个相同类型元素的有序集合(n >= 0)

2. 常见的线性表 - 数组 - 链表 - 队列 - 栈 - 哈希表

线性表之数组 (Array)

  1. 数组是一种顺序存储的线性表,所有元素的内存地址是连续的。
  2. int[] array = new int[]{11, 22, 33} Java中声明数组
  3. 在内存中的表示

4. 可以看到 11,22,33 的内存是连续存储的。

动态数组、接口设计

  1. int size():获取元素的数量
  2. boolean isEmpty():判断数组是否为空
  3. boolean contains(E element):是否包含某个元素
  4. void add(E element):添加元素到数组的末尾
  5. E get(int index):获取某个元素
  6. E set(int index, E element):设置idnex位置的元素
  7. void add(int index, E element):把元素添加到index位置
  8. E remove(int index):删除元素
  9. int indexOf(E element):查看元素的位置
  10. void clear():请空所有元素

删除元素思路

  1. 如果删除的是中间元素,我们需要对size--,并且后面的元素依次向前移动,并且将最后一个元素,设置为 null即可

2. 如图,删除index=3的元素,需要把 4 5 6 元素向前挪动

在指定位置添加元素

  1. 如图:如果在索引2处添加元素,则需要将所以 2、3、4的元素向后挪动,将索引2的地方空出来
  2. 这里要注意,移动顺序需要从最后一个元素,依次递减移动。

动态数组,如何扩容

  1. 当内部数组容量满时,无法再后面追加连续的内存,只能重新 new 申请一个更大的数组,然后将原有的元素挪动到新的数组中。
  2. 然后将原有的引用执行新的数组。

最终实现

public class ArrayList<E> {
    /**
     * 当前数组内元素的数量
     */
    private int size;

    /**
     * 存放元素的数组
     */
    private E[] elements;

    /**
     * 内部数组的默认容量
     */
    private static final int DEFAULT_CAPACITY = 10;
    private static final int ELEMENT_NOT_FOUND = -1;

    public ArrayList() {
        this(DEFAULT_CAPACITY);
    }

    public ArrayList(int capacity) {
        // 如果传递进来的 默认容量太小,默认设置为10
        capacity = capacity < DEFAULT_CAPACITY ? DEFAULT_CAPACITY : capacity;
        elements = (E[]) new Object[capacity];
    }

    /**
     * 获取元素的数量
     */
    public int size() {
        return size;
    }

    /**
     * 数组内的元素是否为空
     *
     * @return
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 是否包含某个元素
     *
     * @param element
     * @return
     */
    public boolean contains(E element) {
        return indexOf(element) != ELEMENT_NOT_FOUND;
    }

    public E get(int index) {
        rangeCheck(index);
        return elements[index];
    }

    /**
     * 覆盖设置index处的元素,并且返回原来的元素
     *
     * @param index
     * @param element
     * @return
     */
    public E set(int index, E element) {

        rangeCheck(index);

        E old = elements[index];
        elements[index] = element;
        return old;
    }

    public void add(E element) {
        ensureCapacity(size + 1);
        elements[size++] = element;
    }

    public void add(int index, E element) {

        rangeCheckForAdd(index);

        // 保持内部数组长度 始终比 size 多一个
        ensureCapacity(size + 1);

        // 从最后一个元素,依次向后移动一格
//        for (int i = size - 1; i >= index; i--) {
//            elements[i + 1] = elements[i];
//        }
        int movedLen = size - index;
        if (movedLen > 0) {
            System.arraycopy(elements, index, elements, index + 1, movedLen);
        }
        // 添加新元素
        elements[index] = element;
        size++;
    }

    public E remove(int index) {

        rangeCheck(index);
        // 取出要移除的元素的值
        E old = elements[index];

        // 将要删除元素后续的元素依次往前挪动一格, 如果这里传的是最后一个元素的索引,不会进入for循环
        // 直接走下面的,将最后一个元素设置为 null
//        for (int i = index + 1; i < size; i++) {
//            elements[i - 1] = elements[i];
//        }
        // 被删除的元素本身不需要移动,所以要减一
        int movedLen = size - index - 1;
        if (movedLen > 0)
            System.arraycopy(elements, index + 1, elements, index, movedLen);

        // 将最后一个空出的元素 设置null gc
//        elements[size - 1] = null;
//        size--;
        // 合并成一行代码
        elements[--size] = null;
        return old;
    }

    public int indexOf(E element) {

        // 因为数组中允许存储 null 值,如果传入的参数为null,找到第一个null值
        if (element == null) {
            for (int i = 0; i < size; i++) {
                if (elements[i] == null)
                    return i;
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (element.equals(elements[i]))
                    return i;
            }
        }

        return ELEMENT_NOT_FOUND;
    }

    public void clear() {
        for (int i = 0; i < size; i++) {
            elements[i] = null;
        }
        size = 0;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < size; i++) {
            if (i != 0) {
                sb.append(", ");
            }
            sb.append(elements[i].toString());
        }
        sb.append("]");
        return sb.toString();
    }

    /**
     * 保证数组 最少 拥有 capacity 大小
     *
     * @param capacity
     */
    private void ensureCapacity(int capacity) {

        int oldCapacity = elements.length;

        // 如果当前容量,已经大于你需要的容量,直接返回
        if (oldCapacity >= capacity)
            return;

        // 需要扩容, 扩容了 1.5 倍, >>1 相当于 除 2^1
        int newCapacity = oldCapacity + (oldCapacity >> 1);

        /**
        // 创建新数组
        E[] newElements = (E[]) new Object[newCapacity];
        // 挪动数组元素到新的数组中去
        System.arraycopy(elements, 0, newElements, 0, size);
        elements = newElements;
         */
        // 上面 3行 可以替换为
        elements = Arrays.copyOf(elements, newCapacity);

        System.out.println("旧容量:" + oldCapacity + ",新容量:" + newCapacity);
    }


    private void rangeCheck(int index) {
        // index 不能 >= 当前元素的个数,最大index为 size - 1
        if (index < 0 || index >= size) {
            outOfBounds(index);
        }
    }

    private void rangeCheckForAdd(int index) {
        if (index < 0 || index > size) {
            outOfBounds(index);
        }
    }

    private void outOfBounds(int index) {
        throw new IndexOutOfBoundsException("Index: " + index + ", size:" + size);
    }
}