Java源码阅读之ArrayList

来源:互联网 发布:mix2 知乎 编辑:程序博客网 时间:2024/05/22 03:03

Java源码阅读之ArrayList

前言:网上有很多关于java源代码的文章,其中不乏逐行分析代码的大牛博文。本文不再赘述每行代码的意思,而是分析一些常用方法的代码段。这样,我们在编程的时候,就能了解函数背后运行的机制,使得我们程序设计得更为合理。

1、知识汇总

ArrayList的核心是:

transient Object[] elementData; 

所有的操作都围绕这个数组进行,因此我们说ArrayList本质上就是一个数组。带着这个思路,理解ArrayList就很容易了。

ArrayList是一个很厉害的数组,它能够动态申请内存。当存储空间不够时,会自动扩容。由于底层是用数组实现的,所以ArrayList的查询快,插入删除慢。同时扩容操作会复制数组中的元素,所以扩容也很耗时。

因此在程序设计时,使用ArrayList前,我们最好知道数组的大小,避免多次扩容。另外,如果程序需要大量的插入和删除操作,建议使用LinkedList。

2、构造方法

构造方法被重载为以下三个:

public ArrayList() {} // 无参构造方法public ArrayList(int initialCapacity){} // 一个参数,表示初始化容量大小public ArrayList(Collection<? extends E> c) {} // 通过集合构造ArrayList 

三个方法的源码分别如下:

/** * 无参构造方法,数组的初始容量设置为10 */public ArrayList() {    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}/** * 通过设置初始容量构造ArrayList         */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 */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;    }}

3、扩容

当ArrayList的空间不够用时,会自动申请内存,叫做扩容。扩容的核心部分如下:

/** * minCapacity:当前需要的容量是minCapacity,扩容后的容量应当大于等于minCapacity */private void grow(int minCapacity) {    int oldCapacity = elementData.length; // 扩容前的容量    int newCapacity = oldCapacity + (oldCapacity >> 1); // 新容量等于扩容前容量的1.5倍    // 如果新容量还不够用,那么就扩容到minCapacity得了    if (newCapacity - minCapacity < 0)        newCapacity = minCapacity;     // 如果新容量超过ArrayList最大容量,那么就用最大容量了(ArrayList的容量总得有个上限吧)    // 插一句:最大容量为:0x7fffffff-8    if (newCapacity - MAX_ARRAY_SIZE > 0)        newCapacity = hugeCapacity(minCapacity);    // 扩容的核心就是这句话,将旧数组里的内容复制到新数组里    // 这个操作很耗时,因此我们应当尽量减少扩容    elementData = Arrays.copyOf(elementData, newCapacity);}

4、插入

向ArrayList中插入元素时,分为在末尾插入和在指定位置插入。插入前需要扩容,在指定位置插入时,还要将数组元素依次向后移动一位,因此在指定位置插入比较耗时。

/** * 在ArrayList末尾添加元素 */public boolean add(E e) {    ensureCapacityInternal(size + 1);  // 添加元素需要扩容    elementData[size++] = e; // 为数组最后一位赋值    return true;}/** * 在指定位置插入元素 */public void add(int index, E element) {    rangeCheckForAdd(index); // 检查插入位置是否在[0, size]之间,size是ArrayList的长度    ensureCapacityInternal(size + 1);  // 扩容    System.arraycopy(elementData, index, elementData, index + 1, size - index); // 复制元素,依次向后串一位    elementData[index] = element; // 给插入位置的元素重新赋值    size++;}

5、删除

在删除指定的元素时,需要将删除位置后面的元素依次向前移动一位,这个操作是耗时的,因此在ArrayList里执行删除是不划算的。除非我们只删除最后一位元素。

/** * 删除指定的元素 */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;}/* * 删除指定位置的元素 */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; // 最后一位设置为null,等待GC}/** * 删除所有元素 */public void clear() {    modCount++;    // clear to let GC do its work    for (int i = 0; i < size; i++)        elementData[i] = null;    size = 0;}

6、修改

修改就是元素重新赋值

/** * 重新为指定元素设置值 */public E set(int index, E element) {    rangeCheck(index); // 范围检查    E oldValue = elementData(index); // 该位置的原始值    elementData[index] = element; // 在指定位置赋值    return oldValue; // 返回原始值}

7、查询

查询某个元素,其核心是一层for循环遍历,时间复杂度O(n)

public boolean contains(Object o) {    return indexOf(o) >= 0;}/** * 返回元素第一次出现的位置,如果不存在则返回-1 */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;}

除了上述方法外,还有addAll()、ArrayList()、isEmpty()等方法,这里不再赘述,如果想查看详细的分析,可以参考:
http://blog.csdn.net/ns_code/article/details/35568011

今天就到这里,拜拜~

这里写图片描述

原创粉丝点击