Java集合框架--ArrayList源码解析(JDK1.7)

ArrayList类是List接口的实现类,ArrayList可以看成一个动态数据,可以根据大小动态变化。由于其数组的底层结构,所以它的访问速度非常快,删除和插入操作较慢。

一、 结构图

下图是ArrayList类在Collection集合框架中的结构图,蓝线代表继承关系,绿线代表接口实现

Java集合框架--ArrayList源码解析(JDK1.7)_第1张图片

  • 继承AbstractList,实现了List。提供了添加、删除、修改、遍历等功能
  • 实现RandomAccess接口,提供了随机访问功能,也就是通过索引快速访问数组元素
  • 实现Cloneable接口,重写clone( )函数,可以被复制
  • 实现java.io.Serializable接口,支持序列化,能通过序列化去传输

二、 ArrayList类简介

1. 定义

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

ArrayList支持泛型

2. 属性

    // 版本号,不用管
    private static final long serialVersionUID = 8683452581122892189L;

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

    // 用于创建一个空的实例
    private static final Object[] EMPTY_ELEMENTDATA = {};

    // 用于保存List数据的数组
    private transient Object[] elementData;

    // 数组大小
    private int size;

ArrayList中有两个重要的属性:elementData 和 size

elementData是Object[]类型的数组

elementData前面有个transient关键字,Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。

3. 构造函数

    // 构造器一
    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

    // 构造器二
    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }

    // 构造器三
    public ArrayList(Collection c) {
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }
  • 第一个构造器:根据指定大小initialCapacity,初始化elementData数组大小
  • 第二个构造器:未指定数组大小,则elementData为空数组EMPTY_ELEMENTDATA
  • 第三个构造器:将提供的集合转成数组返回给elementData(返回若不是Object[]将调用Arrays.copyOf方法将其转为Object[])。

4. 常用API方法摘要

// Collection中定义的API
boolean             add(E object)
boolean             addAll(Collection collection)
void                clear()
boolean             contains(Object object)
boolean             containsAll(Collection collection)
boolean             equals(Object object)
int                 hashCode()
boolean             isEmpty()
boolean             remove(Object object)
boolean             removeAll(Collection collection)
boolean             retainAll(Collection collection)
int                 size()
 T[]             toArray(T[] array)
Object[]            toArray()

// AbstractCollection中定义的API
void                add(int location, E object)
boolean             addAll(int location, Collection collection)
E                   get(int location)
int                 indexOf(Object object)
int                 lastIndexOf(Object object)
ListIterator     listIterator(int location)
ListIterator     listIterator()
E                   remove(int location)
E                   set(int location, E object)
List             subList(int start, int end)

// ArrayList新增的API
Object               clone()
void                 ensureCapacity(int minimumCapacity)
void                 trimToSize()
void                 removeRange(int fromIndex, int toIndex)

从方法列表中可以发现:
- remove() 方法有两个,传参不同返回值也不同,一个根据数组元素值删除返回是否成功,一个根据索引位置删除返回删除的值
- 同样add()、remove()也有两个

三、 源码分析

1. 数组扩容

    //   
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

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

        ensureExplicitCapacity(minCapacity);
    }

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

        // 判断是否需要扩容,并扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }



    private void grow(int minCapacity) {
        // 获取当前数组大小
        int oldCapacity = elementData.length;
        // 获得扩容后的数组大小:原来的1.5倍
        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);
    }
  • 每次在进行插入操作时都会调用ensureCapacity()确保是否需要进行扩容操作,扩容时,每次变为原来的1.5倍大小

2. 添加元素

    // 添加单个元素
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 进行扩容检查
        elementData[size++] = e;
        return true;
    }

    // 在指定位置添加元素
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // 扩容检查

        // 将index位置后面的数组元素统一后移一位,把index位置空出来
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

    // 添加一个集合
    public boolean addAll(Collection c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // 扩容检查
        // 复制数组,将C添加到elementData尾部
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

    // 在指定位置添加一个集合
    public boolean addAll(int index, Collection c) {
        if (index > size || index < 0)
           throw new IndexOutOfBoundsException(
               "Index: " + index + ", Size: " + size);

       Object[] a = c.toArray();
        int numNew = a.length ;
       ensureCapacity( size + numNew);  // 扩容检查

        int numMoved = size - index;
       // 数组复制,空出第index到index+numNum的位置,即将数组index后的元素向右移动numNum个位置
        if (numMoved > 0)
           System. arraycopy(elementData, index, elementData, index + numNew,
                          numMoved);

       // 将要插入的集合元素复制到数组空出的位置中
        System. arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }
  • 指定位置添加元素时,会将该指定位置后面的元素依次后移
  • 可以返现,ArrayList的开销主要都在于数组扩容数组拷贝

3. 删除元素

    // 读取索引位置的元素
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

    // 根据索引位置删除
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        // 复制移动数组,覆盖index位置
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // 把移动后空出的位置置为null

        return oldValue;
    }

    // 根据元素值删除
    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; // clear to let GC do its work
    }


  • 通过删除方法,可以发现ArrayList中允许存储NULL值
  • 通过元素值删除时,只删除查找到的第一个
  • 快速删除fastRemove(int index)和remove(int index)的不同在于返回值
  • 但添加元素时有扩容操作,但是删除元素时却没有缩容操作,那如果数组被删除到很小,那数组中大量空间将会被闲置,这时候ArrayList提供了trimToSize()方法,可以将数组大小设置为当前size。不过这个方法需要手动自己调用,ArrayList中的其他方法不会调用。
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = Arrays.copyOf(elementData, size);
        }
    }

4.其他方法

ArrayList类中很多其他方法,比较简单基本上都是通过for循环进行数组的操作,就不进行分析了。

四、 总结

  • ArrayList是一种通过动态数组实现的List
  • 查找开销是O(1)级别,插入和删除是O(n)级别
  • ArrayList会在每次添加新元素时,自己进行扩容操作,扩展为原来的1.5倍大小

你可能感兴趣的:(Java基础)