java中ArrayList的源码实现

来源:互联网 发布:asc软件 编辑:程序博客网 时间:2024/06/09 17:26

java中的ArrayList底层是通过数组实现的,下面是我对ArrayList源码阅读之后整理并添加了注释之后的整理,为了方便,就只贴出ArrayList的add方法以及它的一些思想还有边界条件处理:

public class ArrayList<E> implements List<E>  {    private Object[] elementData;//存储数据的数组    private int size;//数组的当前个数 而不是最大个数    private static final int DEFAULT_CAPACITY = 10;    private static final Object[] EMPTY_ELEMENTDATA = {};    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};    //为什么最大是2^31-8? As the Array itself needs 8 bytes to stores the size 2147483648(原文)    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE-8;    private boolean RangeCheck(int index){        return (index>-1)&&(index<size);    }    //保证newSize不会越界 将集合的capacity增加到minCapacity minCapacity为所需最小容量    //采用的思路和jdk中源码思路类似 如果不足则添加原来容量的一半 用移位来实现    public void ensureCapacity(int minCapacity) {        //如果elementData为空 最小扩展到10 如果不为空 最小扩展到0        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)?0:DEFAULT_CAPACITY;        if(minCapacity>minExpand){            ensureExplicitCapacity(minCapacity);        }    }    private void ensureCapacityInternal(int minCapacity){        if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);        }        ensureExplicitCapacity(minCapacity);    }    //确保明确的容器    private void ensureExplicitCapacity(int minCapacity){        //如果空间不足 进行扩充        if(minCapacity - elementData.length > 0){            grow(minCapacity);        }    }    //扩充容器    private void grow(int minCapacity){        int oldCapacity = elementData.length;        //扩充1.5倍 原来的数加上原来的数右移一位        int newCapacity = oldCapacity + (oldCapacity>>1);//为什么用右移而不是除以2?因为右移比除以二快        if(newCapacity - minCapacity < 0){            newCapacity = minCapacity;//选出大的一方        }        if(newCapacity - MAX_ARRAY_SIZE > 0){            newCapacity = hugeCapacity(minCapacity);//传入整型太大时候的处理        }        //经过确定扩容的大小后直接创建        elementData = Arrays.copyOf(elementData, newCapacity);    }    //对创建时传入数字太大的情形做出处理    private static int hugeCapacity(int minCapacity){        if(minCapacity < 0){            throw new OutOfMemoryError();        }        //如果1.5倍超出了MAX_ARRAY_SIZE则传入Integer.MAX_VALUE否则传入MAX_ARRAY_SIZE        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;    }        public ArrayList(int initialSize){//带初始化容量大小的构造函数        super();        if(initialSize>0){//大于0直接创建            this.elementData = new Object[initialSize];            //原版jdk没有修改size 为什么不修改size?        }        else if(initialSize == 0){//等于0 直接赋空值            this.elementData = EMPTY_ELEMENTDATA;        }        else{//小于0直接抛出异常            throw new IllegalArgumentException("非法参数:"+initialSize);        }    }    public ArrayList() throws Exception{        this(DEFAULT_CAPACITY);//调用有参构造函数 默认大小为10    }    public ArrayList(Collection<? extends E> c){        elementData = c.toArray();//返回Object[]        size = elementData.length;                if(elementData.getClass() != Object[].class){//如果类型不同 则统一类型            elementData = Arrays.copyOf(elementData, size, Object[].class);//ArrayList的底层实现 数组 Arrays.copyOf() 赋值指定数组        }    }@Override    public boolean add(E e) {       ensureCapacityInternal(size+1);//确保容器空间没问题       elementData[size++]=e;       return true;    }}

在阅读jdk中ArrayList的相关源码实现后的一些感悟:

1、jdk中对边界条件的处理(对边界情况的处理很全面,但是源代码中可生成ArrayList的尺寸的上限为Integer.MAX_VALUE,然而其实达到不了那么大,最大只能是:Integer.MAX_SIZE-8,因为这个ArrayList对应的底层数组本身需要8byte来进行储存)

2、每当ArrayList需要add,但是容量又不够的时候就需要扩容,扩容之后尺寸为扩容前尺寸的1.5倍,即原来尺寸加上原来尺寸右移一位,之所以使用右移一位而不是除以2是因为右移比除以2快

3、当ArrayList的所需要的尺寸特别大的时候,如果不是事先开辟好对应的空间,而是一次一次的添加,则会在添加的过程中一次又一次地遇到需要调用ensureCapacityInternal();的情况,效率很低。所以在使用ListArray的时候最好在初始化的时候传入恰当的尺寸,或者ListArray本身提供了ensureCapacity(int minCapacity)方法,可以一次创建对应的空间,从而很好地提高效率

4、ArrayList底层实际上是通过数组实现,开辟新空间的关键在于Arrays.copyOf(elementData,size),所以对ArrayList的查找和替换的时间复杂度为O(1),然而插入删除的需要涉及到后面大量元素的移动,时间复杂度为O(n)

5、ArrayList的contains()、indexOf()方法以及包含调用了这些方法的containsAll()、retainAll()、removeAll()方法等底层实际上都是使用了继承自Object对象的equals()方法,所以如果想要这些方法能够正常运行,需要保证存储在ArrayList里的数据类型的equals()方法经过了重写,比较的是对象之间的值而不是对象之间的内存地址。

原创粉丝点击