美文网首页
ArrayList源码分析

ArrayList源码分析

作者: nitricoxide | 来源:发表于2021-01-26 13:28 被阅读0次

    1 先看属性

    //默认容量
    private static final int DEFAULT_CAPACITY = 10;
    
    //当用户指定ArrayList容量为0时,返回该空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    
    /**
     * 当用户没有指定ArrayList的容量时,返回的是该数组
     * 当与EMPTY_ELEMENTDATA的区别就是:该数组是默认返回的,而后者是在用户指定容量为0时返回的
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    /**
     * 当用户没有指定ArrayList的容量时,返回的是该数组
     * 当该数组为DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,首次元素添加到ArrayList中时,数组将 
     * 扩容至DEFAULT_CAPACITY大小
     */
    transient Object[] elementData;
    
    /**
     * ArrayList实际存储的数据数量
     */
    private int size;
    

    2 构造方法

    2.1 ArrayList(int initialCapacity)

    构造一个具有指定初始容量的空列表

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            //如果从参数大于0,创建对应大小的数组。
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //如果等于0,返回EMPTY_ELEMENTDATA。
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            //如果是负数,抛异常。
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    

    2.2 ArrayList()

    /**
     * 无参构造函数
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    

    2.3 ArrayList(Collection<? extends E> c)

    public ArrayList(Collection<? extends E> c) {
        //将集合转换为Object[]数组
        elementData = c.toArray();
        //把转化后的数组长度赋值给size属性,并判断是否为0
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            //这句话的意思是:c.toArray可能不会返回Object[],可以查看java官方编号为6260652的bug
            if (elementData.getClass() != Object[].class)
                //若c.toArray()返回的数组类型不是Object[],则利用Arrays.copyOf来构造一个大小为size的Object[]数组
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            //如果为0替换空数组,防止创建过多的空数组
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
    

    3 方法

    3.1 boolean add(E e)

    将指定的元素添加到此列表的尾部

    public boolean add(E e) {
        //将size + 1传递到这个方法,保证资源不浪费
        ensureCapacityInternal(size + 1);  // Increments modCount!!()
        //size存放的是长度,不是下标,先利用原来size修改下标值,再将size++
        elementData[size++] = e;
        return true;
    }
    
    /**
     * 扩容方法(后面会经常见到)
     */
    private void ensureCapacityInternal(int minCapacity) {
        //如果elementData是空数组,则取默认容量和minCapacity的最大值
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
    
        ensureExplicitCapacity(minCapacity);
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
        // 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的
        modCount++;
    
        // overflow-conscious code
        //防止溢出代码,确保指定的最小容量 > 数组缓冲区当前的长度
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    /**
     * 扩容,确保minCapacity至少能存储minCapacity个元素
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //运算符>>是带符号右移,如果oldCapacity = 2, newCapacity = 2 + (2 >> 1) = 2 + 1 = 3
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果newCapacity依旧小于minCapacity则将minCapacity赋值给newCapacity
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果newCapacity大于最大容量,则直接分配Integer.MAX_VALUE
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //Arrays.copyOf():将原来的数组扩容到newCapacity大小,扩容的部分用null填充
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    /**
     * 这个比较简单,如果是负数则报错,传入的参数大于最大容量返回int最大值,否则返回最大容量。
     */
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
    

    3.2 void add(int index, E e)

    将指定的元素插入此列表中的指定位置。

    public void add(int index, E element) {
        //判断角标是否越界(看下面的比较的方法,意思就是角标最大是size,也就是当前最大下标 + 1,
        //如果index == size,那就跟add(E e)效果一样)
        rangeCheckForAdd(index);
        //这个方法上面有
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //这个方法的意思是将原来的数组下标从index到最后,往后都移动一位
        //比如index是2,原数组是[1, 2, 3, 4, null] 执行完后 [1, 2, 3, 3, 4]
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        //修改数组下标为index的值
        elementData[index] = element;
        size++;
    }
    
    /**
     * 添加时检查索引是否越界
     */
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    

    3.3 boolean addAll(Collection<? extends E> c)

    按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。

    public boolean addAll(Collection<? extends E> c) {
        //将集合类变成数组
        Object[] a = c.toArray();
        //获取数组大小(这个大小也是扩容的大小,同时是System.arraycopy()的长度)
        int numNew = a.length;
        //扩容
        ensureCapacityInternal(size + numNew);  // Increments modCount
        //将数组a复制到现有数组,
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }
    

    3.4 boolean addAll(int index, Collection<? extends E> c)

    从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。

    public boolean addAll(int index, Collection<? extends E> c) {
        //检查index是否越界
        rangeCheckForAdd(index);
        
        //转换成数组
        Object[] a = c.toArray();
        int numNew = a.length;
        //扩容
        ensureCapacityInternal(size + numNew);  // Increments modCount
    
        //这个值是需要移动的长度
        int numMoved = size - index;
        //判断是否是从末尾插入
        if (numMoved > 0)
            //将原数组的index位置到最后的数组移动,预留出来numNew长度
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);
    
        //将数组a从下标0开始,插入到原数组,原数组从index开始插入,长度为a.length
        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }
    

    3.5 void clear()

    移除此列表中的所有元素。

    public void clear() {
        modCount++;
    
        // clear to let GC do its work
        //循环把数组中的每个地址都指向null,gc会把每个对象进行回收
        for (int i = 0; i < size; i++)
            elementData[i] = null;
    
        //size置为0,但其实数组长度还是那么多
        size = 0;
    }
    

    3.5 boolean contains(Object o)

    public boolean contains(Object o) {
        //结果大于0返回true
        return indexOf(o) >= 0;
    }
    
    /**
     * 循环比较数组中每个元素的值,返回列表中首次出现的指定元素的下标,如果此列表不包含则返回-1
     */
    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;
    }
    

    3.6 E get(int index)

    返回此列表中指定位置上的元素。

    public E get(int index) {
        //检查数组下标是否越界
        rangeCheck(index);
    
        return elementData(index);
    }
    
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        //返回数组中对应下标元素
        return (E) elementData[index];
    }
    

    3.7 int indexOf(Object o)

    返回列表中首次出现的指定元素的下标,如果此列表不包含则返回-1(其实contains就是利用了该方法)

    /**
     * 循环比较数组中每个元素的值,返回列表中首次出现的指定元素的下标,如果此列表不包含则返回-1
     */
    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;
    }
    

    3.8 int lastIndexOf(Object o)

    返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -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;
    }
    

    3.9 E remove(int index)

    移除此列表中指定位置上的元素。

    public E remove(int index) {
        //判断下标是否越界
        rangeCheck(index);
    
        modCount++;
        //获取被移除的元素,最后返回用
        E oldValue = elementData(index);
    
        //判断移除的元素是否是数组中最后一个元素
        int numMoved = size - index - 1;
        if (numMoved > 0)
            //如果不是,则将原数组index位置之后的元素都往前移动
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //将最后一个元素设置为null,size--
        elementData[--size] = null; // clear to let GC do its work
    
        return oldValue;
    }
    

    3.10 boolean remove(Object o)

    移除此列表中首次出现的指定元素(如果存在)。

    public boolean remove(Object o) {
        //如果需要移除的值是null,那么就循环数组通过==null比较。
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        //如果移除的值是对象,那么就循环数组通过equals进行比较。(这也是为什么说写一个
        //类要重写equals方法,因为默认的equals比较的是地址,如果不重写这种集合类的方法就会失效)
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    
    /**
     * 私有的remove方法,与remove方法相比,没有边界值检查并且不用返回删除的值
     */
    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
    }
    

    3.10 E set(int index, E element)

    用指定的元素替代此列表中指定位置上的元素。(这个注释就不一行一行加了,相信看到这里大家就都能看懂了)

    public E set(int index, E element) {
        rangeCheck(index);
    
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
    

    相关文章

      网友评论

          本文标题:ArrayList源码分析

          本文链接:https://www.haomeiwen.com/subject/xgdkzktx.html