ArrayList源码学习----JDK1.7

什么是ArrayList?


   ArrayList是存储一组数据的集合,底层也是基于数组的方式实现,实际上也是对数组元素的增删改查;它的主要特色是java

  • 有序;(基于数组实现)
  • 随机访问速度快;(进行随机访问的时候,只须要遍历全部的数组)
  • 能够为null;(基于数组实现)
  • 元素能够重复;(基于数组实现)
  • 线程是不安全的;(对于多个线程访问时,没有进行同步操做,之因此不安全也是为它的性能而设计,若是须要建立一个安全的ArrayList集合能够经过 Collections.synchronizedList(list);实现同步)

  

 ArrayList结构分析


 ArrayList类结构以下图:数组

    

 咱们能够看到ArrayList实现了List、RandomAccess、Cloneable、java.io.Serializable四个接口和继承了一个AbstractList抽象类,下面咱们就来进一步的分析它们各自的做用;安全

  • Iterable :实现此接口以便支持foreach语法。
  • Collection:包含了集合基本方法。
  • AbstractCollection:实现了Collection里面大部分方法。
  • List:继承自Collection接口,包含了对集合操做的一些方法。
  • AbstractList:继承自AbstractCollection实现List部分方法。
  • Serializable:序列化的接口。
  • RandomAccess 用来代表其支持快速随机访问。
  • Cloneable:标识其支持对象复制。

 

  ArrayList源码分析


下面咱们主要从ArrayList属性构造方法核心的方法来进行解析:dom

 

ArrayList定义了两个私有属性:源码分析

    /**
     * 用于存储数据的数组
     */
    private transient Object[] elementData;

    /**
     * 数组元素的实际个数
     */
    private int size;

第一个属性主要是存储元素的一个数组, 第二个属性是用于记录数组元素的实际个数,而不是数组的长度。性能

 

ArrayList定义了三个构造方法:学习

    /**
     * 数组初始容量构造方法
     *
     * @param  initialCapacity  
     * @throws IllegalArgumentException 
     *    
     */
    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)    //若是指定的初始容量为负数,将抛出参数异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];    //指定一个存储数组初始容量值为:initialCapacity
    }

    /**
     * 默认构造方法
     */
    public ArrayList() {
        this(10);    //默认给定初始容量为10
    }

    /**
     * 给定一个参数集合的构造方法
     * @param c 
     * @throws NullPointerException 
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();    //转为数组
        size = elementData.length;    //给定size初始值

        if (elementData.getClass() != Object[].class)    // 是否成功转化为Object类型数组
            elementData = Arrays.copyOf(elementData, size, Object[].class);    // 不为Object数组的话就进行复制
    }

ArrayList 核心方法分析this

 

添加元素  add(E e)

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

从源码中看到在添加元素时调用ensureCapacityInternal方法,这个方法呢就是为了保证存储数组的大小够用,不会致使数组越界,下面具体看下ensureCapacityInternal实现spa

    private void ensureCapacityInternal(int minCapacity) {
        modCount++;    // 结构性修改加1
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)    //添加元素时长度 - 存储数组长度是否大于0,若是大于0说明数组大小够用,小于0则须要对存储数组扩容
            grow(minCapacity);
    }

若是存储数组须要进扩容操做,那么就会调用grow方法,这个方法会产生一个新的数组从新赋值给存储数组,新的的数组长度是原数组的1.5倍。下面具体看下grow实现线程

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;    // 原来的容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);    // 新的容量扩展为原来容量的1.5倍
        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);  // 建立一个新的容量数组赋值给存储数组
    }

 

访问元素  get(index)

    public E get(int index) {
        rangeCheck(index);    // 检验索引是否合法

        return elementData(index);
    }

    在根据索引获取元素时,首先进行索引值的判断,索引值大于实际size的大小则抛出异常,不然继续根据索引值获取存储数组中的对象,rangeCheck实现

    private void rangeCheck(int index) {  
        if (index >= size)    // 索引值大于实际size的大小则抛出异常
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

   根据索引值获取存储数组中的对象,elementData实现

    E elementData(int index) {
        return (E) elementData[index];    // 返回存储数组对应索引的值,向下转型(Object -> E)
    }

 

修改元素  set(int index, E element)

    public E set(int index, E element) {
        rangeCheck(index);  // 检验索引是否合法

        E oldValue = elementData(index);   // 原始值
        elementData[index] = element;     // 把存储数组索引为index的值改成新值
        return oldValue;     // 返回原始值
    }

 

插入元素  add(int index, E element)

    public void add(int index, E element) {
        rangeCheckForAdd(index);  // 检验索引是否合法

        ensureCapacityInternal(size + 1);  // 须要扩容扩容操做
    // 拷贝到从下标为index+1位置开始的新的elementData数组中。     // 即将当前位于该位置的元素以及全部后续元素右移一个位置。  
        System.arraycopy(elementData, index, elementData, index + 1,    
                         size - index);
        elementData[index] = element;
        size++;    // 实际元素长度+1
    }

     因为ArrayList 是个动态数组,插入元素时须要移动指针操做,因此插入元素效率低。

 

删除指定位置元素   remove(int index)

    public E remove(int index) {
        rangeCheck(index);    // 检验索引是否合法

        modCount++;    // 结构性修改加1
        E oldValue = elementData(index);  // 获取原来的值

        int numMoved = size - index - 1;  // 须要移动的元素的个数
        if (numMoved > 0)
System.arraycopy(elementData, index
+1, elementData, index, numMoved); elementData[--size] = null; // 最后一个元素设置为空 return oldValue;    // 返回原始值 }

删除元素时一样须要移动指针操做,删除元素效率低。

删除元素时首先判断是否为最后一个元素,若是不是最后一个,就按指定的索引从存储数组删除,索引后面全部元素左移一个位置,并把最后一个元素设置为null以便GC进行回收

 

 总结


不少技术,不仅是只会用就行,研究它底层实现实际上是颇有趣的一件事,同时技术可以获得必定的提高。下篇继续学习LinkedList

若是有不足之处请指出,谢谢!

相关文章
相关标签/搜索