源码分析|ArrayList

1.什么是ArrayLsit

ArrayList是JDK中的一个基于数组实现的线性的可变长度的集合类,并且实现了List接口。

2.ArrayList构造方法分析

不指定容量时

//创建ArrayList
ArrayList list = new ArrayList();

创建完成后,点击进入ArrayList源码,可以发现是

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

这样的一个无参构造方法对ArrayList进行了初始化操作,其中elementData是一个Object类型的数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA也是一个Object类型的数组且长度为0。

指定容量时

//创建ArrayList
ArrayList list = new ArrayList(20);

再次点击就会发现会调用一个制定了初始化容量的构造方法

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

根据代码可知,我们传入参数后会创建一个容量为20的数组。如果传入的不是20而是0的话,就会创建一个容量为空的数组,也就是this.elementData = {}

3.add()方法

//创建ArrayList
ArrayList list = new ArrayList();
list.add("aaa");

点击进入之后可以发现如下代码

public boolean add(E e) {
    //用于检测容量是否够用
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //将元素添加进集合中,并且维护size的值。  size的初始值为0
    elementData[size++] = e;
    return true;
}

点击ensureCapacityInternal(size + 1);会发现如下代码

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

从代码中可以看出传入的是添加元素需要的最小空间,然后再点击calculateCapacity方法

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //DEFAULT_CAPACITY = 10
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

发现传入的是一个Object类型的数组和需要的最小容量。因为elementData是空的,所以会进入第一个if判断,这样会从10和0之间选取最大值,结果显而易见,所以10被返回到ensureExplicitCapacity的参数列表中。

接着再点击ensureExplicitCapacity

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        //扩容函数
        grow(minCapacity);
}

从这里可以看出会将刚才传入的10传到扩容的函数中,再点击grow

private void grow(int minCapacity) {    //minCapacity = 10
    //elementData是一个空数组,所以oldCapacity为0
    int oldCapacity = elementData.length;
    // newCapacity = 0 + 0      所以newCapacity = 0
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 0 - 10 < 0  true
    if (newCapacity - minCapacity < 0)
        // newCapacity = 10
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

从中得出newCapacity = 10,然后调用copyOf方法进行复制,复制完成就会将elementData的长度设置为10。

总结:在第一次调用add()方法时,会将容量初始化为10,但是如果总是执行添加操作,就会再次到达ArrayList的扩容标准

向list中添加元素到11次

    //创建ArrayList
    ArrayList list = new ArrayList();

    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");    //第11次调用add()方法

此时我们再执行上面的操作,直到这里

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // 11 - 10 > 0    true
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

再次执行扩容操作

private void grow(int minCapacity) {    //minCapacity = 11
    // oldCapacity = 10
    int oldCapacity = elementData.length;   
    //newCapacity = 10 + 10 / 2    15
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 15 - 11 < 0   false
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 进行copy
    elementData = Arrays.copyOf(elementData, newCapacity);
}

当拷贝完成扩容操作执行完毕,elememtData的容量此时为15

总结:当容量进行扩容时会将容量扩容为原来的1.5

4.get()方法

System.out.println(list.get(2));

get方法需要传入数组下标,并通过下标返回数组中的值。点击get方法会显示如下内容

public E get(int index) {
    //检查下标合法性,不合法则抛出异常
    rangeCheck(index);
    
    //直接返回数组中下标为index的元素
    return elementData(index);
}

5.set()方法

list.set(2, "bbb");

这里表示想将数组中下标为2的元素设置为bbb。点击进入

public E set(int index, E element) {
    //检查下标合法性
    rangeCheck(index);

    //保存索引为index位置的值
    E oldValue = elementData(index);
    //将传入的元素设置在index位置
    elementData[index] = element;
    //返回保存的元素
    return oldValue;
}

通过传入index和E类型的元素,更新完成后将原来的元素值返回。

6.remove()方法

System.out.println(list.remove(2));

删除下标为2的元素,并将删除前的元素返回,点击remove()方法

public E remove(int 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;
}

7.iterator()方法

    //创建ArrayList
    ArrayList list = new ArrayList();

    list.add("aaa");
    list.add("bbb");
    list.add("ccc");

    //创建迭代器
    Iterator it = list.iterator();
    //判断迭代器中是否还有下一个元素
    while (it.hasNext()){
        //将对象取出
        Object o = it.next();
        System.out.println(o);
    }

迭代器可以遍历ArrayList中的元素,点击iterator()

public Iterator<E> iterator() {
    return new Itr();
}

在迭代器内部创建了一个Itr对象,再点击

//指向元素的游标,当前cursor = 0
int cursor;       // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;

Itr() {}

//迭代器中是否包含下一个元素
public boolean hasNext() {
    return cursor != size;
}

//将游标向后移动
public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    //将cursor向后移动
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

进入之后关注以上方法及参数
因为上面添加了3个元素,所以size为3,cursorsize不相等,返回true,所以会进入while循环并执行next方法,首先会将cursor赋值给i,此时i为0,然后cursor向后移动最后将下标为0的元素返回。一直循环下去直至hasNext返回false

发布了5 篇原创文章 · 获赞 0 · 访问量 27

猜你喜欢

转载自blog.csdn.net/xtxycyc/article/details/104477811