JDK源码阅读—1.ArrayList

2019-11-08  本文已影响0人  繁书_

JDK源码阅读—1.ArrayList

1.全局变量

// 存放数据的Object类型数组
transient Object[] elementData;

// 数组的默认容量
private static final int DEFAULT_CAPACITY = 10;

// 空数组 在使用其他构造方法使用此数组进行初始化
private static final Object[] EMPTY_ELEMENTDATA = {};

// 空数组在使用默认构造方法时会初始elementData为此数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 所允许的数组最大容量,最大容量为Integer最大值减8的原因:
// 1.避免内存溢出,减少出错概率
// 2.因为部分虚拟机会在存储对象头信息,减少的8字节实际就是对象头信息所需要的内存
// 对象头包括两部分信息:Mark Word(标记字段)和 Klass Pointer(类型指针)。 该存储其实只会在部 
// 分虚拟机中才被使用。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

2.构造方法

1.默认构造方法

 public ArrayList() {
     // 集合在初始化时会直接将数组初始话为一个空数组,使用懒加载机制,在集合真正使用时才会计算容量
     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
 }

2.指定容量

/**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            // 如果给定的初始容量大于0,会直接按照给定的容量初始化一个Object类型数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            // 如果给定容量等于0,则初始化一个空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            // 如果小于0,抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

3.指定集合

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            // 在子子类重写父类方法的时候,在不修改返回值的前提下,子类返回了什么类型就是什么类型,
           // 不会向上转换为父类的返回值类型,所以这里得到的类型可能不为Object类型,
            // 比如一个Integer类型的ArrayList,在调用toArray方法时,返回值就是Integer类          
           // 型,而不是Object类型
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

3.常用方法

1.add(E e) 添加元素

public boolean add(E e) {
        // 添加之前进行扩容操作,将当前数组元素个数+1,作为当前所需的最小容量
        // 扩容方法在下面
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 将元素添加到数组尾部
        elementData[size++] = e;
        return true;
}

// 1.确认最小容量 ensureCapacityInternal(minCapacity)
private void ensureCapacityInternal(int minCapacity) {
        // 判断当前数组是否为默认构造方法构造出来空数组
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 在默认容量10和minCapacity中找出一个最大值当作最小容量
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
}

// 2.真正扩容操作 ensureExplicitCapacity(minCapacity)
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        // 判断是否需要扩容,如果当前所需要的容量,已经大于数组的长度,则进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
}

// 3.扩容方法 grow(int minCapacity)
private void grow(int minCapacity) {
        // overflow-conscious code
        // 当前数组的长度
        int oldCapacity = elementData.length;
        // 将原始容量右移一位,也就是将原始容量除以2,然后再加上原始容量。将得出来的结果当作            // 新容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 判断新容量是否小于minCapacity,如果小于,则将minCapacity作为新容量
        if (newCapacity - minCapacity < 0)
            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);
}

// 4.大容量扩容方法 hugeCapacity(minCapacity)
private static int hugeCapacity(int minCapacity) {
        // 判断所需容量是否小于0,如果小于0说明发生了内存溢出
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        // 判断所需容量是否大于所允许的最大值,如果大于则将Integer最大值作为数组长度
        // 如果小于或等于则将所允许的最大值作为数组最大长度
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
}

2.add(int index, E element) 在执行位置插入元素

 public void add(int index, E element) {
        // 检测要插入的下表是否超过数组长度
        rangeCheckForAdd(index);
        // 扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 将原数组在index位置以后的元素全部复制到index+1的位置,这样index位置的元素
        // 就会空出来
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        // 将新元素赋值到index的位置
        elementData[index] = element;
        size++;
 }

3.addAll(Collection<? extends E> c) 将集合内的元素全部添加到当前集合中

public boolean addAll(Collection<? extends E> c) {
        // 将集合转为数组
        Object[] a = c.toArray();
        int numNew = a.length;
        // 重新确认数组新容量
        ensureCapacityInternal(size + numNew);  // Increments modCount
        // 将传入的数组复制到原数组后面
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
}

4.addAll(int index, Collection<? extends E> c) 在指定位置插入新集合

public boolean addAll(int index, Collection<? extends E> c) {
        // 检测下标是否越界
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        // 扩容
        ensureCapacityInternal(size + numNew);  // Increments modCount
        // 计算原数组中要往后移动的下标数量,例:
        // 假设原数组为Object[] a = {1,2,3,4,5},要添加的数组为Object[] b = {7,8};
        // 现在要在下标2的位置将b数组插入到a数组中,首先要将a中的3,4,5元素复制到a的后面
        // 复制后的数组为a = {1,2,3,4,3,4,5},复制的起始位置就是size - index,5-2 = 3,
        // 3就是元素复制过来的起始位置,然后在将b数组复制到a的2,3元素上面,最终结果就是
        // {1,2,7,8,3,4,5}
        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
}

5.indexOf(Object o) 查找指定元素在集合中第一次出现的下标

 public int indexOf(Object o) {
        // 判断给定的元素是否为空,如果为空直接判断是否有元素为空
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            // 遍历集合,如果此下标位置的元素和给定元素相同则返回下标值,否则返回-1
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
 }

6.lastIndexOf(Object o) 从集合尾部开始查

public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
}

7.get(int index) 返回指定下标的元素

 public E get(int index) {
        // 检测下标有无越界
        rangeCheck(index);
        // 返回集合中的此下标的元素
        return elementData(index);
 }

8.remove(int index) 移除指定下标的元素

 public E remove(int index) {
        // 检测下表有无越界
        rangeCheck(index);

        modCount++;
        // 取出此下标位置的元素用来返回
        E oldValue = elementData(index);
     
        // 计算需要复制的元素数量,例:
        // 假设数组为Object[] a = {1,2,3,4,5}, index为2,也就是说要移除元素3,
        // 那么元素3右边的元素全部都要往左移动一个下标,那么要复制的元素个数就是
        // 5-2-1 = 2,将元素4,5复制从index的位置开始复制,复制后的数组为a = {1,2,4,5,5}
        // 最后将末尾元素置空
        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;
 }

9.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才是真正的删除方法
                    // 方法在下面
                    fastRemove(index);
                    return true;
                }
        }
        return false;
}

// fastRemove(int index)
    /*
     * Private remove method that skips bounds checking and does not
     * return the value removed
     */
    private void fastRemove(int index) {
        // 下面方法实际和remove(inde 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
    }

10.clear() 清空元素方法

 public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
 }

11.removeAll(Collection<?> c) 移除包含在给定集合中的所有元素

 public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
 }

// 1.直接看batchRemove 批量删除
/**
 * complement 为false时代表删除与给定集合中相同的元素,
 * 为true时,代表保留与给定集合中相同的元素
 */
private boolean batchRemove(Collection<?> c, boolean complement) {
        // 将集合中的数组赋值转换为局部变量操作,提高效率
        final Object[] elementData = this.elementData;
        // r为循环次数,w为elementData最终的元素数量
        int r = 0, w = 0;
        // 集合有没有进行删除操作
        boolean modified = false;
        try {
            for (; r < size; r++)
                // 循环判断找出原数组内和给定数组内相同的元素,
                if (c.contains(elementData[r]) == complement)
                    // 将不相等的元素放入到elementData中
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            // 只有当try中出现异常才会出现r!=size的情况,当出现异常时,将从r开始往后的元素
            // 全部复制到w起始的位置,这样就会将需要删除的元素给覆盖调,最后将集合中需要保留的            
           // 元素个数计算出来
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            // 判断数组内现有的元素数量是否和原数组的集合数量相同,如果相同,说明没有必要进行删除操作
            if (w != size) {
                // clear to let GC do its work
                // 循环以元素个数为循环起始值,将后面的元素全部清空,
                // 以为w代表数组内元素个数,大于w的下标也就是需要删除的元素
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
}

12.removeRange(int fromIndex, int toIndex) 移除给定下标范围内的元素

/**
 * 这是一个用protected修饰的方法,只能通过子类去调用 
 */
protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        // 计算要移动元素的个数
        // 假如size = 5,fromIndex = 1 toIndex = 3,那么需要toIndex后面的元素都要移动
        // 因为不包含toIndex本身的元素,所以需要移动的元素就是2个元素
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
}
上一篇下一篇

猜你喜欢

热点阅读