java中ArrayList的源码实现

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()方法经过了重写,比较的是对象之间的值而不是对象之间的内存地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值