ArrayLst实现

一、动态数组

 这个图能够说明一些问题,因为数组在堆内存中申请的空间是连续的,所以我们可以根据索引很快的查询到数据。数组的容量是固定的,所以必须实现数组的动态扩容。

二、动态数组的设计

动态数组里包含2个重要的成员变量:size表示元素的数量,elements就是一个定长的数组,所以ArrayList是由数组实现的。我记得有次面试被问到ArrayList里为什么不能放int,只能放Integer,当时被问懵逼了,现在看ArrayList源码就明白了,因为ArrayList设计的是泛型。

接口的设计:

其实主要就是我们最熟悉的:元素添加、删除、修改、查询。

 

构造函数

 /**
     * @param capaticy 用户自定义的容量
     */
    public ArrayList(int capaticy){
        capaticy = (capaticy<DEFAULT_CAPACITY)? DEFAULT_CAPACITY : capaticy;
        elements = (E[])new Object[capaticy];
    }

    /**
     * 默认初始化为10的数组
     */
    public ArrayList(){
        this(DEFAULT_CAPACITY);
    }

用户可以指定arrayList的大小,那么我们需要给他创建个initialCapacity大小的数组。

当用户传负数,我这里会给他初始化一个容量为10的数组。

添加元素

第一种情况是向数组的末尾添加元素,第二种情况是向数组的某个指定位置添加元素,如图我们向index=2的位置添加元素时,那么从索引为2开始的所有元素都要向后面移动一位。

  •  /**
         * 在index位置插入一个元素 index = 3 element=88 size=7
         * @param index   11  22  33  44  55  66  77  ->11  22  33  88  44  55  66  77
         * @param element
         */
        public void add(int index, E element) {
            rangeCheck(index);
            ensureCapacity(size+1); //保证数组的容量是size+1
            for(int i=size;i>index;i--){
                elements[i] =  elements[i-1];
            }
            elements[index] = element;
            size++;
        }

    这里ensureCapacity(size+1)的含义是保证我们数组的容量是size+1,举个例子:假设数组的容量是10,此时的size=10;说明数组中元素存满了,当我们再添加一个元素时,就需要保证数组的容量是size+1

数组扩容:

  

/**
     * 保证要有capacity的容量
     * @param capacity
     */
    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;
    }
  • 由于数组elementData最大的容量只有10, 所以当数组存满元素时, 就需要对数组进行扩容
  • 因为数组是无法动态扩容的, 所以需要创建一个新的数组,这个数组的容量要比之前数组的容量大
  • 然后在将原数组中的元素存放到新数组中, 这样就实现了数组的扩容

clear方法和remove方法的细节:

在clear方法中不仅需要将size改为0,同事需要将对象的值都改为null,而在remove方法中需要将index位置的元素也置为空,这样jvm才会去回收数组中的对象。

ArrayList的时间复杂度分析:

其实就是分析增删查改的时间复杂度,我们经常说这个方法的时间复杂度是O(n),这里的n指的不是参数,而是数据规模,那么arrayList的数据规模是什么呢?毫无疑问是size的大小。

get()和set()时间复杂度都是O(1),很好理解,它们跟size无关。那么另外一个需要解释的问题就是get(1)和get(1000)时间复杂度为什么是一样的呢?原因就是数组里的地址是连续的,假设数组里放的是整数,那么一个元素就占4个字节,编译器就很容易计算出index=100位置的地址:数组的首地址+4*index,所以get(1)和get(100)所需的时间是一样的。

add()和remove()方法是类似的,分析add()就可以了。

假设我们往数组的末尾添加元素,那么add()最好的时间复杂度就是O(1),这里需要注意,在大多数情况下是O(1),在最坏的情况下是O(n),如果数组满了,那么数组需要扩容,所以此时是O(n)。

如果我们往index=0的位置添加元素,那么add()的时间复杂度就是O(size),因为所有的元素都要往前移动,所以add()最坏时间复杂度就是O(n),同理平均时间复杂度也是O(n)。

最后附上ArrayList完整的代码:

package 动态数组;

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;

    /**
     * @param capaticy 用户自定义的容量
     */
    public ArrayList(int capaticy){
        capaticy = (capaticy<DEFAULT_CAPACITY)? DEFAULT_CAPACITY : capaticy;
        elements = (E[])new Object[capaticy];
    }

    /**
     * 默认初始化为10的数组
     */
    public ArrayList(){
        this(DEFAULT_CAPACITY);
    }

    /**
     * 清除所有元素
     */
    public void clear() {
        for(int i=0;i<size;i++){
            elements[i] = null;
        }
        size = 0;
    }

    /**
     * 元素的数量
     * @return
     */
    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;
    }

    /**
     * 添加元素到尾部
     * @param element
     */
    public void add(E element) {
        add(size,element);
    }

    /**
     * 获取index位置的元素
     * @param index
     * @return
     */
    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;
    }

    /**
     * 在index位置插入一个元素 index = 3 element=88 size=7
     * @param index   11  22  33  44  55  66  77  ->11  22  33  88  44  55  66  77
     * @param element
     */
    public void add(int index, E element) {
        rangeCheck(index);
        ensureCapacity(size+1); //保证数组的容量是size+1
        for(int i=size;i>index;i--){
            elements[i] =  elements[i-1];
        }
        elements[index] = element;
        size++;
    }

    /**
     *
     * 删除index位置的元素 index = 3 size=7
     * @param index    11  22  33  44  55  66  77
     * @return
     */
    public E remove(int index) {
        rangeCheck(index); //检查索引
        E element = elements[index];
        for(int i=size+1;i<size;i++){
            elements[i-1] = elements[i];
        }
        size--;
        elements[size] =null; //处理索引为7的元素,将其变为null
        return element;
    }

    /**
     * 查看元素的索引
     * @param element
     * @return
     */
    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;
    }

    /**
     * 保证要有capacity的容量
     * @param capacity
     */
    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;
    }
    
    private void rangeCheck(int index) {
        if(index<0 || index > size){
            throw new IndexOutOfBoundsException("数组越界了");
        }
    }
    

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

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值