ArrayList源码

Author Avatar
cuteximi 7月 22, 2017
  • 在其它设备中阅读本文章

简介:

1.ArrayList是基于数组的,一个动态“数组”,能够自动的扩容;

2.ArrayLIst不线程安全的,只能在单线程下使用,要想在多线程环境下使用,
可以使用集合操作类Colletcions.syncronized(List l)方法转化为一个线程安全的ArrayList
或者使用concurrent下面的CopyOnWriteArrayList类

3.ArrayList实现了序列化接口Serializable接口,能够序列化传输;实现了RandomAcess接口,支持快速随机访问,(实际上就是用过下标号进行访问);实现了Cloneable接口,能够被克隆

源码(JDK8)+部分注释


package java.util;

import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//代码略
}

属性


    /**
     *序列化版本号
     */
    private static final long serialVersionUID = 8683452581122892189L;
    /**
     * Default initial capacity.
     * 默认初始化的容量 10
     */
    private static final int DEFAULT_CAPACITY = 10;

    private static final Object[] EMPTY_ELEMENTDATA = {};

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存放元素的数组,从这可以看出来底层就是数组
     */
    transient Object[] elementData;

    /**
     * The size of the ArrayList (the number of elements it contains).
     * 数组包含元素的个数
     */
      private int size;
     /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     * 数组的最大上限
     */

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

ArrayList的属性不多,重要的是elementData,声明这个属性的时候使用了transient关键字, transient用来表示一个域不是该对象序行化的一部分。

ArrayList在序列化的时候会调用writeObject,直接将size和element写入ObjectOutputStream;反序列化时调用readObject,从ObjectInputStream获取size和element,再恢复到elementData。

QA:为什么不直接用elementData来序列化??

原因在于elementData是一个缓存数组,他通常会预留一些空间,等容量不足时再扩容,那么有些空间可能并没有实际存在的数据,使用上述方式,实现序列化的时候,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间;

方法

构造方法


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

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    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;
        }
    }

1.用户自定义大小

带int类型的构造函数
如果传入参数,则代表指定ArrayList的初始数组长度,传入参数如果是大于等于0,则使用用户的参数初始化,如果用户传入的参数小于0,则抛出异常;

2.无参构造器

注意:初始化之后,此时,elementData的长度是1,size是0,那么默认值什么时候起作用呢? :)
在add的时候,elementData变成默认的长度;(参考源码可见)

3.带Collection对象的构造函数

将Colletion对象转换为数组,然后将数组的地址给elementData;如果elmentData长度为0,直接将 EMPTY_ELEMENTDATA赋给它;如果size的值大于0,则执行Arrays.copy方法,把collection对象的内容(可以理解为深拷贝)copy到elementData中

注意:elementData = c.toArray(); 这里执行的简单赋值时浅拷贝,所以要执行Arrays.copyOf 做深拷贝

get方法


    public E get(int index) {
        //判断是否越界
        rangeCheck(index);
        //返回
        return elementData(index);
    }

    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    //数组索引
    E elementData(int index) {
        return (E) elementData[index];
    }


从上面可以看出:
实际上就是从数组中取值,时间复杂度O(1)

add方法

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

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

        ensureExplicitCapacity(minCapacity);
    }

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(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);
    }

如上:添加元素时,为了保证有足够的空间添加元素,如果是第一次添加,会将elementData的大小默认为10;如果不是第一次添加元素,就会跳入ensureExplicitCapacity方法,判断比较需要的空间与当前elementData的大小,如果不够,则扩容,即跳入grow()方法,增值原来的1.5倍;此时时间复杂度O(1)

    public void add(int index, E element) {
            //检查越界
            rangeCheckForAdd(index);

            ensureCapacityInternal(size + 1);  // Increments modCount!!
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            elementData[index] = element;
            size++;
        }    

注意:时间复杂度O(n);

set方法

    public E set(int index, E element) {
            rangeCheck(index);

            E oldValue = elementData(index);
            elementData[index] = element;
            return oldValue;
        }

set会把原来位置上的数据返回;

remove方法


     public E remove(int index) {
            rangeCheck(index);

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

            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

            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
    }

1.remove分两种:index和对象

根据index移除对象,如果是末尾的数据,直接将该位置的元素=null;如果是其他位置,需要将位置后面的元素全部往前移动一个位置,最后将最后一个位置=null;

根据对象,实现找到该对象对应的位置,然后根据位置去删除;(之后,同上);时间复杂度O(n)

indexOf()和lastIndexOf()

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


    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

一个是从前往后找,一个是从后往前找;时间复杂度是O(n)。

subList()

     public List<E> subList(int fromIndex, int toIndex) {
            subListRangeCheck(fromIndex, toIndex, size);
            return new SubList(this, 0, fromIndex, toIndex);
        }

内部类SubList

trimToSize()

 public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

去掉多余的空间;

iterator()

     public Iterator<E> iterator() {
            return new Itr();
        }

内部类Ttr

Vecter

构造函数

     public Vector(int initialCapacity, int capacityIncrement) {
            super();
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            this.elementData = new Object[initialCapacity];
            this.capacityIncrement = capacityIncrement;
        }


    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }


    public Vector() {
        this(10);
    }

与ArrayList不同的是,Vector在初始化是就创建了数组;

grow()

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

在没有制定capacityIncrement的情况下,默认扩容方式是2倍;指定了扩容增长因子之后,每次增capacityIncrement大小的容量;

总结

ArrayList线程不安全的;Vector线程安全;写累了,歇会;

This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://blog.cuteximi.com/ArrayList源码/