首页 > 学院 > 开发设计 > 正文

ArrayList源码剖析,解开神秘面纱之扩容(一)

2019-11-06 06:52:25
字体:
来源:转载
供稿:网友

1. ArrayList的构造方法

1.1. 无参

 public ArrayList() {        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;    }这是无参的构造方法,在后面会讨论到的ArrayList在执行add()方法添加元素时,底层会默认创建一个长度为10的数组

1.2. 自定义数组长度

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

这个构造方法可以自定义数组的长度,如果传进的参数为0,就使用默认的数组长度,即为10

1.3. 带一个Conllection类型的参数

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

2. ArrayList的add()

2.1. 带一个参数的add()

2.1.1. ArrayList允许添加进值为null的元素

public boolean add(E e) {        ensureCapacityInternal(size + 1);  // Increments modCount!!        elementData[size++] = e;        return true;    }从这段源码中可以看出e允许为null,即ArrayList允许添加进值为null的元素

通过以下代码来测试,以验证上述特性

public class Test {   		public static void main(String[] args){		ArrayList<String> arrayList = new ArrayList<String>();		arrayList.add(null);		System.out.PRintln(arrayList.get(0));			}}运行结果如下图

2.1.2. ArrayList是通过创建一个新数组来扩容,大小为原来的1.5倍

从上面的源码可看出add()中调用了ensureCapacityInternal(int minCapacity),现继续往下跟踪探究

 private void ensureCapacityInternal(int minCapacity) {        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);        }        ensureExplicitCapacity(minCapacity);    }minCapacity为当前ArrayList的长度,在ensureCapacityInternal()中可以看出minCapacity总是大于或者等于默认长度

再往下跟踪ensureExplicitCapacity(int minCapacity)

private void ensureExplicitCapacity(int minCapacity) {        modCount++;        // overflow-conscious code        if (minCapacity - elementData.length > 0)            grow(minCapacity);    }从上面这段源码可以看出,当minCapacity>elementData.length时就需要扩容,不同于HashMap的通过负载因子来判断是否需要扩容。

其中的重头戏在于grow(int minCapacity),继续往下跟踪

 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);    }其中的int newCapacity = oldCapacity + (oldCapacity >> 1);可以看出扩容后newCapacity为oldCapacity的1.5倍,newCapacity即为新数组的长度,然后通过Arrays.copyOf(elementData, newCapacity);创建出一个长度为newCapacity新的数组,并将原来数组的元素复制到新数组中

再通过copyOf()继续往下跟踪

public static <T> T[] copyOf(T[] original, int newLength) {        return (T[]) copyOf(original, newLength, original.getClass());    }里面是调用了重载的copyOf,继续跟踪

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {        @SuppressWarnings("unchecked")        T[] copy = ((Object)newType == (Object)Object[].class)            ? (T[]) new Object[newLength]            : (T[]) Array.newInstance(newType.getComponentType(), newLength);        System.arraycopy(original, 0, copy, 0,                         Math.min(original.length, newLength));        return copy;    }在这段源码中可以看出copyOf的底层是调用一个native方法, System.arraycopy(),这个方法不创建新的数组,而是把一个数组的元素,通过设置的起始位置和个数,复制到另一个对应的数组,这个数组为copyOf()创建,由于 System.arraycopy()是本地方法,所以效率会更高。

2.2. 带两个参数的add()

这个add()是对指定的位置插入元素,先看相应的源码

public void add(int index, E element) {        rangeCheckForAdd(index);//对index的合法性进行判断        ensureCapacityInternal(size + 1);  // Increments modCount!!        System.arraycopy(elementData, index, elementData, index + 1,                         size - index);        elementData[index] = element;        size++;    }其中的重点依旧是native方法System.arraycopy(),它使得index索引位置开始的元素都往后退一位,然后将新的元素插入index的索引位置

3. 总结

3.1

ArrayList通过不同的构造方法可以选择3种中其中的一种形式创建,第一种是不带参,创建默认长度为10的属猪;第二种是可以自定义数组的长度;第三种是传入一个Conllection类型的参数。

3.2

ArrayList在add()时判断是否需要扩容,如果需要扩容则扩为原来的1.5倍,底层是依次调用copyOf()和System.arraycopy()创建一个新长度的数组,然后将旧数组的元素复制到新数组中去


上一篇:2017-03-06

下一篇:关于OOP设计模式简介

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表