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

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

一、 结构图

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

这里写图片描述

  • 继承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<? extends E> 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<? extends E> 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> T[]             toArray(T[] array)
Object[]            toArray()

// AbstractCollection中定义的API
void                add(int location, E object)
boolean             addAll(int location, Collection<? extends E> collection)
E                   get(int location)
int                 indexOf(Object object)
int                 lastIndexOf(Object object)
ListIterator<E>     listIterator(int location)
ListIterator<E>     listIterator()
E                   remove(int location)
E                   set(int location, E object)
List<E>             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<? extends E> 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<? extends E> 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倍大小
阅读更多
版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/m0_37241851/article/details/59488596
文章标签: java jdk 框架 源码
个人分类: Java基础
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭