【数据结构】ArrayList源码笔记

关键变量

private static final int DEFAULT_CAPACITY = 10;

默认初始化容量


private static final Object[] EMPTY_ELEMENTDATA = {};

当指定长度为0时,返回的空数组


private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

当调用无参构造方法,返回的是该数组。刚创建一个ArrayList 时,其内数据量为0。它与EMPTY_ELEMENTDATA的区别就是:该数组是默认返回的,而EMPTY_ELEMENTDATA是在用户指定容量为0时返回。


transient Object[] elementData

存储集合元素的底层实现:真正存放元素的数组,可以理解为他就是ArrayList这个壳下的数组,下文称之为操作数组


private int size;

ArrayList的实际大小



贯穿全类的方法

public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

Object src : 原数组
int srcPos : 从元数据的起始位置开始
Object dest : 目标数组
int destPos : 目标数组的开始起始位置
int length : 要copy的数组的长度

    // 源数组
    byte[]  srcBytes = new byte[]{2,4,0,0,0,0,0,10,15,50};  
    // 目标数组
    byte[] destBytes = new byte[5]; 

    //创建一个一维空数组,数组的总长度为 5位,然后将srcBytes源数
    //组中 从0位 到 第5位之间的数值 copy 到 destBytes目标数组
    //中,在目标数组的第0位开始放置.
    System.arrayCopy(srcBytes,0,destBytes ,0,5)



构造函数

1、ArrayList()

    public ArrayList() {
        //只是简单将空数组赋值给操作数组
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

2、ArrayList(int initialCapacity)

    public ArrayList(int initialCapacity) {
        //长度大于0则指定一个initialCapacity长度的数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //如果长度为0,也是赋值了空数组,但是与无参的数组
            //不同
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

3、ArrayList(Collection<? extends E> c)

    public ArrayList(Collection<? extends E> c) {
        //直接通过toArray转化为数组
        elementData = c.toArray();
        因为size代表的是集合元素数量,所以通过别的集合来构
        //造ArrayList时,要给size赋值
        if ((size = elementData.length) != 0) {
            if (elementData.getClass() != Object[].class)
                //如果toArray失败,则通过拷贝的方式初始化操作
                //数组
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            //否则为空数组
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }



关键方法


add(E e)

    public boolean add(E e) {
        //在每次添加数据之前,都会通过 size+1 去判断加了之后是
        //不是需要扩容,是的话先进行扩容,再增加元素
        ensureCapacityInternal(size + 1);  
        elementData[size++] = e;
        return true;
    }

add(int index, E element)

    public void add(int index, E element) {
        //越界判断
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        //一样在新增之前判断是否需要扩容
        ensureCapacityInternal(size + 1);  
        //将index开始的数据 向后移动一位,腾出位置给index插
        //插入数据
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        //长度 + 1
        size++;
    }

addAll(Collection<? extends E> c)

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

ensureCapacityInternal(int minCapacity)

    private void ensureCapacityInternal(int minCapacity) {
        //这也就是为什么默认构造器的空数组与手动设置的不一样
        //将容量设置为minCapacity与初始容量两者的最大值
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //
        ensureExplicitCapacity(minCapacity);
    }

ensureExplicitCapacity(int minCapacity)

    private void ensureExplicitCapacity(int minCapacity) {
        //结构改变次数(长度是否变化),现在是add操作,所以+1
        //与是否需要扩容没有关系的
        modCount++; 
        
        //“添加完数据后的最小容量”比当前数组长度还要大,说明添
        //加了这条数据后,数组是放不下这么多元素的,需要进行扩
        //容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

grow(int minCapacity)

    private void grow(int minCapacity) {
        //旧数组的长度
        int oldCapacity = elementData.length;
        //1、这行很重要!!!
        //新数组的长度为:旧数组长度 + 旧数组长度右移 1 位
        //因为右移相当于除以2,所以:
        //新数组长度为:旧数组 + 旧数组长度的一半
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //2、这里也很重要!!!
        //如果扩容后的长度还是不够
        //那就直接将“添加完数据后的最小容量”作为新数组长度
        //相当于:不管了,反正下次add也会触发扩容...
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)       
            newCapacity = hugeCapacity(minCapacity);
        //3、这里也是!!!
        //这个方法的作用是:传入一个旧的数组(带元素),传入一
        //个新的长度得到一个新数组:长度是新的长度,元素是旧
        //的元素
        //也就是相当于拉长了数组长度(扩容)
        elementData = Arrays.copyOf(elementData, newCapacity);
    }



remove(int index)

    public E remove(int index) {
        //越界处理
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        //操作数+1
        modCount++;

        //取出要删除的那个元素
        E oldValue = (E) elementData[index];

        //计算出删除后需要移动的元素个数
        //例如:size = 10 ,index = 6,那删除后需要移动的元
        //素就是:10-6=4,后面四位,但是!size是长度,index是
        //下标,所以要算的话得统一算,这里再-1算是将size从长度
        //“转成”下标 
        int numMoved = size - index - 1;
        //拷贝数组并覆盖
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //将最后一个元素置空
        //原注释:clear to let GC do its work:
        //空了之后就没有跟gcroot有关联了,就会被检测到
        elementData[--size] = null; 

        return oldValue;
    }

remove(Object o)

    public boolean remove(Object o) {
        //这里并不是判空,而是因为ArrayList可以存储null
        //所以这里得if和else都是在找出对应的元素,并执行删除
        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;
    }

removeAll(Collection<?> c)

    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        //两个方法用到batchRemove:
        //1、removeAll:删除列表中与c列表元素相同的元素
        //2、retainAll:保留列表中与c列表元素相同的元素
        return batchRemove(c, false);
    }

batchRemove(Collection<?> c, boolean complement)

    private boolean batchRemove(Collection<?> c, boolean complement) {
        //复制一份数组
        final Object[] elementData = this.elementData;
        //r是for的循环数,也是elementData的下标
        //w是保留下来的数的下标
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                //如果c不包含elementData[0、1、2...]
                if (c.contains(elementData[r]) == complement)
                    //则将这个元素保留下来,并存在以w为递增
                    //数的下标中
                    elementData[w++] = elementData[r];
        } finally {
            //try里面异常了,会导致 r!=size
            if (r != size) {
                //将出现异常处后面的数据全部复制覆盖到数组里。
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
                //置空数组后面的元素
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }



set(int index, E element)

    public E set(int index, E element) {
        //因为不涉及修改结构,所以不会修改modCount
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

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



get(int index)

    public E get(int index) {
        rangeCheck(index);//越界检查
        return elementData(index); //下标取数据
    }



清空

clear()

    public void clear() {
        modCount++;

        //置空让垃圾回收器检测到
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值