List之ArrayList集合源码分析

List集合之ArrayList集合分析

这里方便阅读代码,加深印象。自定义List接口,自定义ArrayList实现自定义接口。LsArrayList类模仿ArrayList类实现了LsList的核心接口:size、add、get、remove,并分析ArrayList源码。

自定义List接口,定义核心接口

public interface LsList<E> {

    /**
     * 定义数组长度
     */
    int size();

    /**
     * 添加元素
     * @param e
     * @return
     */
    boolean add(E e);

    /**
     * 使用下标查询元素
     * @param index
     * @return
     */
    E get(int index);

    /**
     * 删除元素
     * @return
     */
    E remove(int index);

}

LsArrayList实现LsList接口

public class LsArrayList<E> implements LsList<E>{

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 初始化大小为0
      */
    private int size;

    /**
     * 调用add方法时,为10
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * transient表示elementData数组不能被序列化,elementData存放数组所有数据
     */
    transient Object[] elementData;

    /**
     * 初始化集合为空的数组
     */
    public LsArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }


    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        // 如果数组元素为空,给他默认大小为10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        //  modCount++; 作增删时 变量++保证安全性

        // 10-0>0 扩容 判断是否需要扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * 扩容
     * @param minCapacity
     */
    private void grow(int minCapacity) {
        // 获取数组长度 oldCapacity原容量 newCapacity新容量 原容量=0
        int oldCapacity = elementData.length;
        // 新容量=原容量+原容量向右位移1位  相当于原容量/2   oldCapacity >> 1 二进制右移 >>1 /2 || <<1 *2    0
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 新容量-最小容量 = 0-10<0 新容量=-10
        if (newCapacity - minCapacity < 0) {
            // 第一次对数组做初始化容量操作 newCapacity=10
            newCapacity = minCapacity;
        }
        // 扩容,将旧的数据复制到新的数组中
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    @Override
    public E get(int index) {
        // 检查下标是否越界
        rangeCheck(index);
        return (E) elementData[index];
    }

    @Override
    public E remove(int index) {
        // 检查下标是否越界
        rangeCheck(index);

        // modCount++;

        // 获取要删除的对象
        E oldValue = get(index);

        // 计算需要移动的位置
        int numMoved = size - index - 1;

        // 判断需要删除的元素不是最后一个元素,将需要删除的元素后面的所有元素往前移动一位
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                    numMoved);
        // numMoved为0时,也就说明 需要删除的是最后一个元素,直接将最后一个数据置为null,让GC回收
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

    /**
     * 检查下标是否越界
     * @param index
     */
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException("下标位置越界了!index为:"+index);
    }
}

ArrayList数组移除元素图解

举例: 若此时有一个装有五个元素(突然好想你、顽固、好好、天使、知足)分别对应下标为(0、1、2、3、4)的数组,并对该数组作移除元素操作。大致可分为两种情况:需要移除的元素下标为数组最后一个位置和需要移除的元素下标不是最后一个元素。

移除最后一个元素(下标为4)

移除最后一位元素

需要移除的元素下标为数组最后一个位置:直接将该下标的元素置为null,让GC回收。

移除第一个元素(下标为0)

移除第一个元素

需要移除的元素下标为数组第一个位置:将下标分别为1234的元素往前移动一个位置,将最后一个元素置为null,让GC回收。

移除第二个元素(下标为1)

移除第二个元素

System的arraycopy方法参数作用

public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length);
src 原数组
srcPos 原数组该位置作为起始位置开始复制
dest 目标数组、destPos 目标数组的起始位置
length 要复制数组的长度

测试

public class TestLsArrayList {

    public static void main(String[] args) {
        LsArrayList<String> list = new LsArrayList<>();
        list.add("突然好想你");
        list.add("顽固");
        list.add("好好");
        list.add("天使");
        list.add("知足");
        // 移除元素
        list.remove(3);

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        System.out.println(list.size());

        System.out.println(list.get(4));
    }
}

运行结果

突然好想你
顽固
好好
知足
4
Exception in thread “main” java.lang.IndexOutOfBoundsException: 下标位置越界了!index为:4

总结

  1. ArrayList集合查询、修改效率高。因为可以直接根据下标位置定位到该数据。时间复杂度为O(1)。
  2. ArrayList集合新增、删除效率低。因为如果在集合中间插入或删除数据,该数据后的数据的下标位置需要重新排序,所以效率低。

如文章有误,欢迎批评指正,欢迎交流。

参考:蚂蚁课堂集合源码分析视频。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值