01-ArrayList

ArrayList

一、简述

  • ArrayList是基于数组实现的List集合,内部使用数组保存元素,支持根据下标随机访问元素,并能动态扩容

二、属性和构造方法

2.1 属性

    //默认容量
    private static final int DEFAULT_CAPACITY = 10;
     
    //保存元素数组 
    transient Object[] elementData; // non-private to simplify nested class access

    //元素个数
    private int size;
    
    //空数组,构造方法默认就是构造一个空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    //元素最大个数
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

2.2 构造方法

    //默认构造的时候初始化一个空的数组
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    //指定容量的构造方法
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

三、重要方法

3.1 添加元素

  • add元素之前,会检查容量是否充足,如果不足,在ensureCapacityInternal的逻辑中会进行扩容操作。
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,size - index);
        elementData[index] = element;
        size++;
    }

3.2 删除元素

  • 删除元素。我们看到删除一个元素之后,会将该元素后面的元素全部往前移动一个位置,如果是根据value移除,则需要先遍历所有元素找到value对应的index下标,然后再移除,移除也需要调用arraycopy拷贝index后面的元素,因此移除元素的复杂度是:O(N)
public E remove(int index) {
        //1.rangeCheck会判断index是否越界
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

3.3 修改元素

  • 根据下标修改元素很快,直接通过index定位
    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

3.4 查询元素

  • 根据index查询元素速度很快,复杂度为:O(1)
  • 根据value查询元素需要遍历数组,复杂度为:O(N)
  • lastIndexOf查询value的时候就是从数组末尾开始查询
public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

3.5 扩容

  • 在添加元素的时候,需要保证内部的数组拥有足够的存储空间,因此需要扩容,扩容触发的时机是当前元素已经不足以存下新加入的元素,也就是说数组空间用完了之后才会触发扩容。 每次扩容后的空间是原空间的1.5倍,也就是增长50%的空间。
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        
        //1.计算新的容量为旧的容量的1.5倍(oldLen + oldLen/2 )
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        
        //2.检验新容量参数的合法性
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
         
        //3.拷贝
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

3.6 其他工具方法

  • size、isEmpty、contains、clean
public int size() {
    return size;
}
    
public boolean isEmpty() {
    return size == 0;
}

public boolean contains(Object o) {
    return indexOf(o) >= 0;
}


public void clear() {
    modCount++;
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;
    size = 0;
}
  • toArray:将ArrayList转换为数组并返回
public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

四、小结

  • ArrayList默认容量是10,加入元素的时候会判断容量,如果容量不够会进行扩容,扩容后的容量是扩容前的1.5倍
  • ArrayList底层大量使用System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved)这个本地方法来进行数组元素的复制移动操作,比如在指定下标增删元素之后,会对该下标后面的元素进行移动操作。
  • ArrayList通过index下标读写元素很快(包括访问和修改),复杂度是O(1),但是在指定下标的增删元素,复杂度是O(N),小结是读写快,增删慢,不过如果是直接add在尾部追加元素是很快的。
操作复杂度
根据index读取O(1)
根据index修改O(1)
根据index新增O(N)
根据index删除O(N)
根据value查询O(N)
add尾部追加O(1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值