数据结构之动态数组

标签: 学点算法

数组是一种顺序存储的线性表,所有元素的内存地址是连续的。在很多编程语言中数组都无法动态修改容量,我们今天就简单实现一下可以动态改变容量的数组。

首先实现一个实体类来作为操作对象

public class Person {
    private int age;
    private String name;

    public Person(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //重写 toString 方法用来打印
    @Override
    public String toString() {
        return "Person {age=" + age + ", name=" + name + "}";
    }

    //重写 finalize 方法用来监控销毁状态
    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();

        System.out.println("我被销毁了");
    }

    //重写 equals 方法用来比较对象
    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub

        //如果是空或者不是 Person 对象
        if(obj == null || !(obj instanceof Person)) return false;

        Person person = (Person)obj;
        return (this.age == person.age && this.name.equals(person.name));
    }
}

实现动态数组

其实也就是 增删改查

需要注意的几点

  • 1.)注意判断范围 一般情况下判断的范围都是不小于0,并且小于最大元素数(因为是已经存在的元素,所以范围是 0 到 size - 1,所以不能等于 size);但是在某个位置添加的时候范围可以等于最大元素数(因为是不存在的,插入范围是 0 到 size)。

    /**
     * 判断范围
     * @param index
     */
    private void rangeCheck(int index) {
        if(index < 0 || index >= size) {
            outOfBounds(index);
        }
    }
    
    /**
     * 判断添加的范围
     * @param index
     */
    private void rangeCheckAdd(int index) {
        if(index < 0 || index > size) {
            outOfBounds(index);
        }
    }
  • 2.)注意添加/删除某个位置元素时的挪动顺序 添加的时候先挪动索引最大的元素; 删除的时候先挪动索引最小的元素。

  • 3.)注意扩容 就是新创建一个大空间的数组,将老数组中的元素转移过去

  • 4.)移除的时候最后一个位置要清空 如果最后一个位置不设置为 null 的话会导致挪动之后最后一个位置所指向的对象无法销毁; 还有先减 size 再清空最后一个元素,毕竟是从 0 开始记的。

  • 5.)清空的时候需要将元素置空 存储的是对象的话其实是存储的指向堆空间的地址,所以要将元素对象置空,可以清除内存空间/

最终代码

public class ArrayList<E> {
    private int size = 0;
    private E[] elements;

    private static final int DEFAULT_CAPATICY= 10;
    private static final int ELEMENT_NOT_FOUND= -1;

    //构造方法
    public ArrayList(int capaticy) {
        capaticy = (capaticy < DEFAULT_CAPATICY) ? DEFAULT_CAPATICY : capaticy; 
        elements = (E[])new Object[capaticy];
    }

    //构造方法
    public ArrayList() {
        this(DEFAULT_CAPATICY);
    }

    public int size() {
        return size;
    }

    /**
     * @return
     */
    public boolean isEmpty() {
        return size == 0;
    }

    public boolean contains(E element) {
        return indexOf(element) != ELEMENT_NOT_FOUND;
    }

    public void add(E element) {
        add(size, element);
    }

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

    public E set(int index, E element) {
        rangeCheck(index);
        E old = elements[index];
        elements[index] = element;
        return old;
    }

    public void add(int index, E element) {
        //注意范围
        CheckAdd(index);
        //保证容量(扩容)
        ensureCapacity(size);
//      for (int i = size - 1; i >= index; i--) {
//          elements[i + 1] = elements[i];
        //改进,减少计算
        for (int i = size; i > index; i--) {
            elements[i] = elements[i - 1];
        }
        elements[index] = element;
        size ++;
    }

    //移除某个位置的元素
    public E remove(int index) {
        rangeCheck(index);

        E old = elements[index];
        //
//      for (int i = index + 1; i <= size - 1; i++) {
        //改进,减少计算
        for (int i = index + 1; i < size; i++) {
            elements[i - 1] = elements[i];
        }
        size --;

        //把最后一个元素清空,思考
        elements[size] = null;

        return old;
    }

    //移除元素
    public void remove(E element) {
        remove(indexOf(element));
    }

    //这里注意对象的比较
    public int indexOf(E element) {
        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() {
        ///如果存对象就不能简简单单的把size置为0了
        for (int i = 0; i < size; i++) {
            //销毁数组里的对象
            elements[i] = null;
        }
        size = 0;
    }

    /**
     * 抛出异常
     * @param index
     */
    private void outOfBounds(int index) {
        throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
    }

    /**
     * 判断范围
     * @param index
     */
    private void rangeCheck(int index) {
        if(index < 0 || index >= size) {
            outOfBounds(index);
        }
    }

    /**
     * 判断范围
     * @param index
     */
    private void rangeCheckAdd(int index) {
        if(index < 0 || index > size) {
            outOfBounds(index);
        }
    }

    /**
     * 扩容
     * @param capacity
     */
    private void ensureCapacity(int capacity) {
        int oldCapacity = elements.length;
        if(oldCapacity >= capacity) {
            return;
        }
        int newCapacity = oldCapacity + oldCapacity >> 1; // 1 + 1/2 个老的空间,相当于乘以1.5倍,这样写效率高于直接乘以 1.5 的浮点运算
        E[] newElements = (E[])new Object[newCapacity];
        //把老的元素转移到心的数组中
        for (int i = 0; i < elements.length; i++) {
            newElements[i] = elements[i];
        }
        elements = newElements;

    }

    //重写 toString 用于打印
    @Override
    public String toString() {
        //对于字符串拼接来说更有效率
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Size=").append(size).append(", [");
        for (int i = 0; i < size; i++) {
            if(i !=0 ) {
                stringBuilder.append(", ");
            }
            stringBuilder.append(elements[i].toString());
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }
}