JDK类库源码分析系列3--集合类分析(1) List集合3-ArrayList

一、ArrayList的结构

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

​ 其继承的AbstractList,同时又实现RandomAccess这个算法标记接口。

二、成员变量

1、DEFAULT_CAPACITY

private static final int DEFAULT_CAPACITY = 10;

​ 这个是初始化的时候的默认容量。

2、EMPTY_ELEMENTDATA

private static final Object[] 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);
    }
}

​ 这个是一个默认的空数组实例。

3、DEFAULTCAPACITY_EMPTY_ELEMENTDATA

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

​ 这个也是用来在最开始初始化的,可以看到其在"EMPTY_ELEMENTDATA",加了"DEFAULT_CAPACITY",也就是说这个ArrayList用默认的容量去初始化,有这两种构造函数可以看到,如果传入了初始化的容量,传入为0则用EMPTY_ELEMENTDATA这个初始数组。

4、elementData

transient Object[] elementData; // non-private to simplify nested class access

​ 这个Object数组就是用来放ArrayList添加的元素了,可以看到其的其的作用范围是default,与其处于同一包下的其他类也可以直接访问。

5、size

private int size;

​ 该ArrayList中目前有多少元素。

6、MAX_ARRAY_SIZE

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

​ 该ArrayList允许的最大容量。

三、构造函数

1、ArrayList(int initialCapacity)

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

​ 这个构造函数使用传入的initialCapacity去创建一个对应容量是在再设置给elementData,如果为0,则使用EMPTY_ELEMENTDATA,如果<0则抛出异常不合法的参数 IllegalArgumentException。

2、ArrayList()

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

​ 这个我们前面有简单提到提到,这种在我们第一次调用add方法的时候就会使用DEFAULTCAPACITY_EMPTY去初始化一个默认容量的对象数组。

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

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // defend against c.toArray (incorrectly) not returning Object[]
        // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

​ 这个构造方法是将集合中的元素转换为一个数组再赋值给elementData,然后这里还多了一步,如果elementData.getClass()不是数组对象,则将其copy为对象数组再赋值给elementDate。如果集合入参为空,则将elementData初始化为EMPTY_ELEMENTDATA对象。

四、方法

1、add(E e)

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}

​ 这个方法是按顺序添加一个元素,可以看到其首先是modCount++,记录该ArrayList已经多修改了一次。然后调用add(e, elementData, size)方法去添加新元素。

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}

​ 这个是一个私有方法,如果目前放入的元素已经等于elementData数组的大小,则调用grow()方法去扩容,如果还没有满,则在数组对应s位置赋值,再将size大小+1;

2、add(int index, E element)

public void add(int index, E element) {
    rangeCheckForAdd(index);
    modCount++;
    final int s;
    Object[] elementData;
    if ((s = size) == (elementData = this.elementData).length)
        elementData = grow();
    System.arraycopy(elementData, index,
                     elementData, index + 1,
                     s - index);
    elementData[index] = element;
    size = s + 1;
}
private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

​ 这个方法就是在指定的index位置添加(或替换)元素。首先是通过rangeCheckForAdd判断看其是否越界,在modCount自加,在判断如果size与elementData的长度相同,则需要对elementData进行扩容了,扩容后,再通过System.arraycopy将当前index位置的元素及其之后的元素往后移一位。然后在elementData的index位置将element添加,再size+1。

3、trimToSize

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    }
}

​ 这个就是将elementData变为实际长度为size的设置,因为size表明的是目前在elementData实际放了多少个元素了,而elementData是扩容的时候固定的。这个方法就可以用来回收多余的空间。

4、ensureCapacity

public void ensureCapacity(int minCapacity) {
    if (minCapacity > elementData.length
        && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
             && minCapacity <= DEFAULT_CAPACITY)) {
        modCount++;
        grow(minCapacity);
    }
}

​ 这个就是根据给定的最小容量minCapacity的值去进行扩容,可以看到其是有条件的,需要大于目前elementData的长度,并且目前的elementData要不为DEFAULTCAPACITY_EMPTY_ELEMENTDATA&这个minCapacity要大于默认的DEFAULT_CAPACITY大小。

5、grow(int minCapacity)

private Object[] grow(int minCapacity) {
    return elementData = Arrays.copyOf(elementData,
                                       newCapacity(minCapacity));
}

​ 这个就是根据minCapacity去进行扩容,其调用newCapacity方法。

6、grow()

private Object[] grow() {
    return grow(size + 1);
}

​ 这个去前面对应,minCapacity = size+1。

7、newCapacity

private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity <= 0) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return minCapacity;
    }
    return (newCapacity - MAX_ARRAY_SIZE <= 0)
        ? newCapacity
        : hugeCapacity(minCapacity);
}

​ 这个首先是获取原来elementData的长度,然后新的容量为原来容量的1.5( oldCapacity + (oldCapacity >> 1) ),然后判断newCapacity与传入的miniCapacity的大小,如果newCapacity更大,则使用newCapacity,不过下面还会再判断一次(可能出现大于MAX_ARRAY_SIZE的情况),如果传入的minCapacity更大,则先判断当前elementData是不是DEFAULTCAPACITY_EMPTY_ELEMENTDATA(创建对象的时候使用的是没有参数的构造方法,同时本次是第一次添加元素),则调用Math.max方法选取二者中更大的值。如果不是第一次了,则直接返回minCapacity。

8、hugeCapacity

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE)
        ? Integer.MAX_VALUE
        : MAX_ARRAY_SIZE;
}

​ 这个方法是判断minCapacity是不是已经大于MAX_ARRAY_SIZE了,如果大于则返回int类型的最大值,没有则返回MAX_ARRAY_SIZE。

9、elementData(int index)

@SuppressWarnings("unchecked")
E elementData(int index) {
    return (E) elementData[index];
}

​ 返回index位置的元素,可以看到这个方法的作用于是default。

10、elementAt(Object[] es, int index)

static <E> E elementAt(Object[] es, int index) {
    return (E) es[index];
}

​ 获取es对象数组index位置的值,作用域为default。

11、get(int index)

public E get(int index) {
    Objects.checkIndex(index, size);
    return elementData(index);
}

​ 获取index位置的元素,其首先进行范围判断,再调用elementData(index)方法获取对应元素返回。

12、set(int index, E element)

public E set(int index, E element) {
    Objects.checkIndex(index, size);
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

​ 将元素设置到index位置同时返回该位置原来的元素。

13、remove(int index)

public E remove(int index) {
    Objects.checkIndex(index, size);

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

​ 这个方法是删除指定index位置的元素,并将原来该位置的元素返回。这里的numMoved是用来判断这个index对于size(index为最后一个),这里之所以要再-1是因为size是从1开始,而index是从0开始的,如果不是最后一个,则通过System.arraycopy方法将elementData中的元素从index+开始往后的元素向前移动一位,再将最后一个元素置为null,size-1。

14、remove(Object o)

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

​ 这个方法是删除指定元素(只删除一个),不会返回原来旧的元素。可以看到其for遍历调用equals方法判断,再调用fastRemove方法去设置index位置的元素。

15、fastRemove(int index)

private void fastRemove(int index) {
    modCount++;
    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
}

​ 这个其实与前面的remove(int index)方法没有很大的区别,这个主要就是不会获取原来元素返回

16、clear()

public void clear() {
    modCount++;
    final Object[] es = elementData;
    for (int to = size, i = size = 0; i < to; i++)
        es[i] = null;
}

​ 清理elementData中的元素。其只是简单的遍历置为null

17、removeRange(int fromIndex, int toIndex)

protected void removeRange(int fromIndex, int toIndex) {
    if (fromIndex > toIndex) {
        throw new IndexOutOfBoundsException(
                outOfBoundsMsg(fromIndex, toIndex));
    }
    modCount++;
    shiftTailOverGap(elementData, fromIndex, toIndex);
}
private void shiftTailOverGap(Object[] es, int lo, int hi) {
    System.arraycopy(es, hi, es, lo, size - hi);
    for (int to = size, i = (size -= hi - lo); i < to; i++)
        es[i] = null;
}

​ 删除fromIndex-toIndex之间的元素(不包括toIndex位置的值),可以看到其主要是将(size - hi)元素前移(hi后面的元素),在将最后面的这些元素置为null。

​ 通过上面的这些元素我们也可以明显感觉到,使用ArrayList进行数据的插入、删除指定位置效率不高,因为其涉及到范围数据的整体copy移动。

18、removeAll(Collection<?> c)

public boolean removeAll(Collection<?> c) {
    return batchRemove(c, false, 0, size);
}

​ 设置c集合中元素。

19、retainAll(Collection<?> c)

public boolean retainAll(Collection<?> c) {
    return batchRemove(c, true, 0, size);
}

​ 保留c集合中的元素(不再c中的元素都删除)。

20、batchRemove(Collection<?> c, boolean complement,final int from, final int end)

boolean batchRemove(Collection<?> c, boolean complement,
                    final int from, final int end) {
    Objects.requireNonNull(c);
    final Object[] es = elementData;
    final boolean modified;
    int r;
    // Optimize for initial run of survivors
    for (r = from; r < end && c.contains(es[r]) == complement; r++)
        ;
    if (modified = (r < end)) {
        int w = r++;
        try {
            for (Object e; r < end; r++)
                if (c.contains(e = es[r]) == complement)
                    es[w++] = e;
        } catch (Throwable ex) {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            System.arraycopy(es, r, es, w, end - r);
            w += end - r;
            throw ex;
        } finally {
            modCount += end - w;
            shiftTailOverGap(es, w, end);
        }
    }
    return modified;
}

​ 这个方法就是前面两个调用的方法,其中入参complement是决定用来保留还是用来删除的。

1)、如果传入的complement为false

​ 就表示数据集合中参数,在第一个for循环的时候,就是找到第一个待删除的元素在elementData中位置(c.contains(es[r]) == false ,就一直循环)赋值给r。再将这个位置值赋值给一个新的局部变量w,然后r++将r调整到下一个位置,再从r开始遍历,遇到下一个不需要删除的元素(不在c中的元素)就赋值给w位置,再w++,以此循环。最后再通过shiftTailOverGap(es, w, end)方法,将最后位置w-end长度的元素删除。

2)、complement为false

​ 这个就与上面的相反了,将在c中的元素往前移。

21、readObject(java.io.ObjectInputStream s)&writeObject(java.io.ObjectOutputStream s)

​ 这两个就是从流中读取或者添加元素。

22、listIterator(int index)、listIterator()、iterator()

public ListIterator<E> listIterator(int index) {
    rangeCheckForAdd(index);
    return new ListItr(index);
}
public ListIterator<E> listIterator() {
    return new ListItr(0);
}
public Iterator<E> iterator() {
    return new Itr();
}

​ 这两个方法就是获取遍历的迭代器实现。

五、ArrayList对于Iterator的实现

private class ListItr extends Itr implements ListIterator<E> {
    ListItr(int index) {
        super();
        cursor = index;
    }
private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

​ ArrayList对于Iterator的实现有Itr、ListIterator,其大体逻辑与AbstractList中的实现差不多,这里就不再展开了(但其不是简单在原来写的那样去直接调用get、set方法等,而可能将get、set逻辑直接写在next这些方法中),我们直接来看下其在Itr中对next方法的实现。

1、next()

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 = i + 1;
    return (E) elementData[lastRet = i];
}

​ 可以看到其整体逻辑与AbstractList的实现是类似的,这里是用的lastRet直接从elementData中获取了对应元素。

2、remove()

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

​ 这个删除方法直接调用的ArrayList.this.remove(lastRet)方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值