源码分析|ArrayList

1.什么是ArrayLsit

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

2.ArrayList构造方法分析

不指定容量时

//建立ArrayList
ArrayList list = new ArrayList();
复制代码

建立完成后,点击进入ArrayList源码,能够发现是bash

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
复制代码

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

指定容量时

//建立ArrayList
ArrayList list = new ArrayList(20);
复制代码

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

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 = {}this

3.add()方法

//建立ArrayList
ArrayList list = new ArrayList();
list.add("aaa");
复制代码

点击进入以后能够发现以下代码spa

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

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

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

相关文章
相关标签/搜索