ArrayList源码学习

                                               ArrayList源码学习

对于ArrayList我们都很熟悉,使用起来非常的方便,使用的较多的方法有add(),remove(),indexOf()等,对于这种优秀的集合框架,研究其源码能让我们对其掌握更加深刻,能更合理的应用在业务场景中,同时我们自己在写程序时也能够参考其设计思想,提供我们的编码水平。

一.ArrayList简介

1.ArrayList底层是数组队列,相当于是动态数组,即它是基于数组实现的一个List类。

2.该类封装了一个Object[]数组,capacity属性代表此数组的长度,通过ensureCapacity方法可以一次性增加capacity,能减少增加重分配的次数来提高性能。

3.类的继承结构和层次关系,如下图:

如图

继承了AbstracList抽象类,实现了List接口。List中定义上层接口,AbstracList对一些通用方法进行了实现,ArrayList对自己特有的方法再进行实现。

实现了RandomAccess接口,RandomAccess接口是一个标志接口,表明实现这个接口的List集合是支持快速随机访问的。

实现了Cloneable接口,即覆盖了Clone()函数,能被克隆。

实现了Serializable接口,表明其能被序列化,能通过序列化去传输。

另ArraList是非线程安全的,想保证线程安全可以使用Vector或CopyOnWriteArrayList。

二.源码解析

1.属性和构造方法:

    /**
     * 版本号   
     */
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * 默认初始容量大小
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空数组(用于空实例)。
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

     //用于默认大小空实例的共享空数组实例。
     //我们把它从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量需要增加多少。
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 保存ArrayList数据的数组
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * ArrayList 所包含的元素个数
     */
    private int size;


    /**
     * 带初始容量参数的构造函数。(用户自己指定容量)
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            //创建initialCapacity大小的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //创建空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     *默认构造函数,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为0.初始化为10,也就是说初始其实是空数            
     *组 当添加第一个元素的时候数组容量才变成10
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
     */
    public ArrayList(Collection<? extends E> c) {
        //
        elementData = c.toArray();
        //如果指定集合元素个数不为0
        if ((size = elementData.length) != 0) {
            // c.toArray 可能返回的不是Object类型的数组所以加上下面的语句用于判断,
            //这里用到了反射里面的getClass()方法
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 用空数组代替
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

2.核心方法

2.1 add()方法

boolean add(E e)方法源码分析

    //在list末尾添加指定元素
    public boolean add(E e) {
        //确定list容量是否足够,size+1是因为新添加了一个元素
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //确保容量充足后size++,再将新元素放到size++位置
        elementData[size++] = e;
        return true;
    }

    //确定容量是否充足的方法
    private void ensureCapacityInternal(int minCapacity) {
        //看elementData数组是否是空数组,如果是,minCapacity这里应该等于size+1,即1
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //DEFAULT_CAPACITY=10,所以这里实际上就是让minCapacity=10,也就是初始化大小
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        
        //确认数组容量,接着往下分析
        ensureExplicitCapacity(minCapacity);
    }

    
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        
        //minCapacity如果大于实际elementData的长度,则说明容量是不够用的,需要扩容
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            //真正进行扩容的方法,还得接着看
            grow(minCapacity);
    }

    private void grow(int minCapacity) {
        // overflow-conscious code
        //扩容前elementData的长度
        int oldCapacity = elementData.length;
        //扩容后的长度为原先长度的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        
        //这里的判断是因为elementData最初为空数组时,newCapacity为0,而minCapacity为10,可以
        //看出就是为了最初初始化数组
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;

        //当newCapacity超过了最大值,则调用hugeCapacity方法,将能给的最大值赋予newCapacity
        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);
    }
void add(int index, E element)源码分析
     //在特定位置插入元素
    public void add(int index, E element) {
        //检查插入的位置是否合理
        rangeCheckForAdd(index);
        
        //和上面的分析一样
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        
        //此方法用于插入元素之后,要将index之后的元素全部后移一位
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        
        //将元素放置在index位置
        elementData[index] = element;
        size++;
    }

    /**
     * A version of rangeCheck used by add and addAll.
     */
    private void rangeCheckForAdd(int index) {
        //如果index不在正常范围内则抛出异常
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

2.2 remove()方法

remove(int index)方法
    //删除指定位置上的元素
    public E remove(int index) {
        //检查index范围是否合理
        rangeCheck(index);
        
        //用来检查快速失败的一个标志
        modCount++;

        //直接通过下标找到元素
        E oldValue = elementData(index);

        //计算要移动多少个元素
        int numMoved = size - index - 1;
        if (numMoved > 0)
            //用来移动元素的具体方法
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        
        //将--size上的元素赋为null,让gc更快的回收
        elementData[--size] = null; // clear to let GC do its work
        
        //返回删除的元素
        return oldValue;
    }

 

remove(Object o)方法

    //通过传入的元素来删除list中的此元素,原理就是遍历数组,查找到此元素后将其索引传给fastRemove
    //方法,其内部实现与remove(int index)基本一致
    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;
    }
removeAll(Collection<?> c)方法
    //批量删除
    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        //主要看batchRemove方法的实现
        return batchRemove(c, false);
    }

    
   //这个方法,用于两处地方,如果complement为false,则用于removeAll,如果为true,则给            
   retainAll()用,retainAll()是用来检测两个集合是否有交集的。
   private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData; //将原集合,记名为A
        int r = 0, w = 0;   //r用来控制循环,w是记录有多少个交集
        boolean modified = false;  
        try {
            for (; r < size; r++)
                //参数中的集合C检测是否包含集合A中的元素
                if (c.contains(elementData[r]) == complement)
                    //有的话,就给集合A
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            //如果contains方法使用过程报异常
            if (r != size) {
                //将剩下的元素都赋值给集合A,
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
    //这里有两个用途,在removeAll()时,w一直为0,就直接跟clear一样,全是为null。
    //retainAll():没有一个交集返回true,有交集但不全交也返回true,而两个集合相等的时候,返回            
    false,所以不能根据返回值来确认两个集合是否有交集,而是通过原集合的大小是否发生改变来判断,如    
    果原集合中还有元素,则代表有交集,而元集合没有元素了,说明两个集合没有交集。
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

2.3 set()方法

   public E set(int index, E element) {
        //检查index范围是否正常
        rangeCheck(index);

        //获取旧值
        E oldValue = elementData(index);

        //赋新值
        elementData[index] = element;

        //返回旧值
        return oldValue;
    }

2.4 indexOf()方法

    public int indexOf(Object o) {
        if (o == null) {
            //如果元素为null,则遍历数组,找到第一个为null的元素,返回其下标
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            //如果元素不为null,则遍历数组,找到第一个和指定元素相等的元素,返回其下标
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        //如果元素不存在则返回-1
        return -1;
    }

2.4 get()方法

    public E get(int index) {
        //检查index范围是否正常
        rangeCheck(index);

        //根据数组下标返回元素
        return elementData(index);
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值