JDK之ArrayList源码解读(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_32523587/article/details/85881510

源码基于JDB_1.8版本。

目录

构造函数1

构造函数2

构造函数3

contains(Object o)

lastIndexOf(Object o)

toArray()

get(int index)

set(int index, E element)

add(E e)

add(int index, E element)

clone()


构造函数1

 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本质上就是一个数组,数据类型是Object。当传入的参数大于0时,创建一个大小为初始值的数组;如果参数等于0,则创建一个空数组

private static final Object[] EMPTY_ELEMENTDATA = {};

其他情况下,抛出异常。

构造函数2

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

不传参时,也同样创建一个空数组

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

构造函数3

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

通过另一个Collection来初始化ArrayList。当这个Collection的类型也是数组类型时,直接通过=来赋值。如果不是,则通过Arrays.copyOf的方法来赋值。

contains(Object o)

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

检测ArrayList是否包含某个对象,通过索引该对象在ArrayList中的位置来判断是否包含。如果位置>=零,说明对象在ArrayList中。其中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 {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

当该对象为null时,遍历ArrayList,遇到第一个值为null的位置时,返回该位置的索引;

当该对象不为null时,遍历ArrayList,遇到第一个值与该对象相等的位置时,返回该位置的索引。

当遍历完所有位置都未找到相等的位置时,返回-1。

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

返回对象在ArrayList中的最后一个位置。该方法和indexOf(Object o)的方法基本一样,只不过遍历的时候是从ArrayList的末尾处往前遍历。

toArray()

public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

ArrayList转换成字节数组,直接调用Arrays.copyOf方法。

get(int index)

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

        return elementData(index);
    }

该方法获取ArrayList指定位置的值,调用了rangeCheck(index)和elementData(index)。

private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

该方法校验index的值是否超过ArrayList,超过就抛出数组越界错误。

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

该方法直接返回数组在指定位置的值,因为ArrayList本质上就是数组。

set(int index, E element)

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

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

先检查传入的索引参数是否越界。再获取ArrayList中原来在这个位置上的值oldValue,然后再将这个位置上的值替换成新的值,返回旧值oldValue。

add(E e)

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

先调用ensureCapacityInternal,然后将数组的最大位置的下一位设置成传入的值,返回true表示插入成功。

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

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

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

该方法的作用是确认组成ArrayList里的元素个数是否超过数组的容量。如果数组是空数组,则minCapacity取DEFAULT_CAPACITY和传入的参数中比较大的那个值。例如new ArrayList(2),而数组默认的初始值是10,则minCapacity赋值为10。如果minCapacity大于数组的长度,这个时候需要将数组扩容,因为数组的大小是不可变的,因此需要重新创建一个数组,将原数组的内容赋值过去。扩容方法是grow(minCapacity)。modCount记录集合的修改次数,也就每次add或者remove它的值都会加1。 

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        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);
    }

每次按照原数组50%的大小进行扩容。如果增大50%后的数组大小依然小于传入的参数minCapacity的话,则就将该参数minCapacity作为新数组的大小(一般不会发生)。如果新数组的大小超过了Integer.MAX_VALUE - 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超过了Integer.MAX_VALUE就会变成负数,此时抛出内存溢出;

否则,Integer.MAX_VALUE - 8  < minCapacity < Integer.MAX_VALUE,这个时候,创建一个Integer.MAX_VALUE长度的数组。

然后通过Arrays.copyOf创建一个新数组,将原数组复制过去。

add(int index, E element)

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

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

 作用:在指定位置插入元素。

首先通过rangeCheckForAdd检查位置索引是否合法

private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

如果位置索引大于ArrayList的元素个数或者小于0,抛出数组越界异常。

再通过ensureCapacityInternal检查数组是否已满、是否需要扩容(前面add方法已介绍过)。

然后调用System.arraycopy方法,这是一个native方法,作用是:将数组中index处往后的所有元素都往后面移动一个位置,一共需要移动size - index个元素。然后再将新元素放在数组中index的位置上,同时ArrayList的元素个数加1。

clone()

public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

通过Arrays.copyOf创建一个大小一样的新数组,将原数组复制过去,同时将modCount重置为0。

猜你喜欢

转载自blog.csdn.net/qq_32523587/article/details/85881510