ArrayList(还在不断摸索)

全部是个人探讨源码的个人理解,如果有错误,麻烦一定要在评论区指正呀,给孩子改正的机会。

欢迎评论区讨论不同的看法。

目录

实现接口:

变量:

方法:

ArrayList无参构造方法:

ArrayList(int initialCapacity)有参构造方法:传入一个initialCapacity

ArrayList(Collection c)有参构造方法:传入一个集合

grow():

get():

set():

add(E e)方法:

add(int index, E element)方法:

remove(int index)方法:删除对应下标的元素

remove(Object o)方法:

clean方法:

addAll(Collection c)方法:

addAll(int index, Collection c)方法:

toString()方法:


实现接口:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{}

1、Serializable(序列化)

2、Cloneable(拷贝)浅拷贝和深拷贝

3、RandomAccess(表示其随机访问(get方式)的效率高于顺序访问(迭代器))

RandomAccess接口在应用场景中,如果查询时是一个海量的集合,可以判断是否实现了RandomAccess接口,如果实现了,优先使用随机访问,否则使用顺序访问。

4、List

ArrayList:存储的是元素的内存地址,内存地址指向堆中的对象。

变量:

private static final long serialVersionUID = 8683452581122892189L;//序列化id
//默认容量
private static final int DEFAULT_CAPACITY = 10;
//空的数组容器
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认的空数组容器
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
数组容器实例变量
transient Object[] elementData; 
数组容器的大小(不是数组的长度!!)
private int size;

方法:

平时用的比较多的优先解释。

ArrayList无参构造方法:

会把默认容量为空的数组赋值给当前数组容器,这样在添加元素时会判断是不是该数组,如果是则调用grow()创建一个长度为10的数组。如果不是,代表本身数组容器已经被创建出来了。
 

public ArrayList() {

        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}

minCapacity(数组当前元素个数+1)

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//判断是不是默认为空的数组
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

ArrayList(int initialCapacity)有参构造方法:传入一个initialCapacity

如果大于0,会对应创建相应数组容量的数组(可以小于10),如果为0,则是赋值一个空数组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);
    }
}

ArrayList(Collection<? extends E> c)有参构造方法:传入一个集合

调用集合toArray(),判断集合是否为空,如果为空,把EMPTY_ELEMENTDATA复制给当前容器数组,如果不为空,则继续判断,数组类型是否是Object类型,如过不是则拷贝为Object类型的数组。

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();//将集合转化为数组
    if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

grow():

完成数组的拷贝,还有无参构造时创建长度为10的数组长度

private void grow(int minCapacity) {//传入当前容量
    int oldCapacity = elementData.length;//当前数组长度
    int newCapacity = oldCapacity + (oldCapacity >> 1);//数组长度扩大
    if (newCapacity - minCapacity < 0)//扩大后的长度小于传入的长度,就给予传入的长度
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)//判断长度是否大于最大值
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);//数组拷贝
}

get():

获取对应数组下标到元素

public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}


set():

简单到数组赋值,返回原来元素

public E set(int index, E element) {
    rangeCheck(index);

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

add(E e)方法:

如果是顺序添加元素,则可以优化数组插入数据的缺点,不会涉及到数组的拷贝

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  
    elementData[size++] = e;//赋值对应的下标
    return true;
}
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;//集合修改的次数,这个是继承父类AbstractList的实例变量

    if (minCapacity - elementData.length > 0)
        grow(minCapacity);//如果加入元素后的容量不足,扩容原来的数组
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //如果是默认为空的数组,则把默认为10的容量和minCapacity比较大小,因为后面
        //添加一个集合的时候,容量可能会超过10,这样就创建minCapacity长度的数组
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

添加第一个元素后,如果是无参初始化,则会创建一个容量为10的数组来存储元素,如果继续添加元素,则会判断元素容量是否大于容器数组长度:

如果大于进行数组拷贝grow(),如果当前数组长度为偶数,拷贝为原来数组的1.5倍,如果为奇数,则不会是1.5倍,因为扩容的数组长度是原数组长度右移一位。

如果小于,则是单独的添加元素到数组中。

扩容的倍速原理代码:
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);

add(int index, E element)方法:

对应下标插入元素,会涉及到数组的拷贝,不推荐使用,性能很差。

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

    ensureCapacityInternal(size + 1); 
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);//插入数组的元素后面的后移
    elementData[index] = element;
    size++;
}

remove(int index)方法:删除对应下标的元素

public E remove(int index) {
    rangeCheck(index);//判断是否越界,如果越界抛异常

    modCount++;//修改次数+1
    E oldValue = elementData(index);/取出对应下标要删除的元素

    int numMoved = size - index - 1;//对应下标后元素的个数
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
//数组拷贝,把删除元素后的元素拷贝到新的数组前一个位置
//arraycopy(原数组,原数组起始位置,目标数组,目标起始位置,复制元素的个数)
    elementData[--size] = null; // 让GC清理

    return oldValue;//返回对应下标要删除的元素
}

remove(Object o)方法:

删除数组中第一个对象,遍历数组,先找到第一个该对象的下标,如果找不到就返回false,找到再进行删除,fastRemove和remove差不多

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;
}
private void fastRemove(int index) {//真正删除是此方法,和remove(int index)差不多
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null;
}

clean方法:

保存数组长度,让gc清理里面的元素,因为如果堆中的对象没有引用,则gc会清理对象。

public void clear() {
    modCount++;
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

addAll(Collection c)方法:

将传入的集合转化为数组a,判断添加a数组的长度加上元素的个数是否大于原数组长度elementData.length,如果大于进行扩容,如果扩容后仍不足以存储,则扩容为size+numNew,然后将a数组复制到原数组elementData上,从size开始,复制numNew个元素,然后修改elementData的元素个数,方法返回ture的话说明,插入成功,如果返回false,说明添加的集合元素为0。

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;
}

addAll(int index, Collection<? extends E> c)方法:

配合addAll(Collection c)一起理解,改动一些。

public boolean addAll(int index, Collection<? extends E> c) {
//检查下标是否在0到size,不是则抛异常,这里可以等于size,因为进行add就是
//在数组后或者前添加元素,区别rangeCheck(index),后者是不可以等于size。 
    rangeCheckForAdd(index);
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew); 
    int numMoved = size - index;//需要移动到元素
      //判断移动到元素大于0到话,先把需要移动到元素拷贝到新数组后
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);
    //这说明不需要移动,所以直接在后面进行数组拷贝
    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}

toString()方法:

因为ArrayList中没有toString方法,所以继承的是父类的父类AbstractCollection中的toString方法

public String toString() {
    Iterator<E> it = iterator();
    if (! it.hasNext())//如果没有元素
        return "[]";

    StringBuilder sb = new StringBuilder();//使用StringBuilder不用频繁创建String对象
    sb.append('[');
    for (;;) {//无限循环
        E e = it.next();
        sb.append(e == this ? "(this Collection)" : e);//三目运算符
        if (! it.hasNext())//如果没有元素
            return sb.append(']').toString();
        sb.append(',').append(' ');//如果有元素
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值