ArrayList

当我还在初学java的阶段的时候,看别人的代码,会发现ArrayList出场率非常高,仿佛ArrayList和List是绑定一样的,"List<String> list = new ArrayList<>();",那时候天真的我没有细究这些集合的异同,但是在工作两年之后,每次选用一个集合的时候,我都会思考以下几个问题:

1.我这个集合是什么样的规模?

2.我对这个集合会做什么操作?

3.高并发场景下直接使用会出现什么问题?

...

介绍

ArrayList的底层实现是数组,确切说的是可动态扩容的数组

image-20210502232252836

数组特性

既然是数组,那就会继承数组的优势,随机访问

随机访问一般指随机存取。在计算机科学中,随机存取(有时亦称直接访问)代表同一时间访问一组序列中的一个随意组件。

从这个优势可以知道频繁对集合内元素访问的场景使用ArrayList。

动态扩容能力

接下来就来看数组是如何动态扩容的

image-20210502235233707

当需要的数组的容量大于现有数组容量,就要对现有的elementData进行扩容,贴一下扩容的源码:

/**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code ->考虑溢出,jdk源码经常出现
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);// 1.5倍扩容,做计算尽量使用位移,性能考虑
        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);// 可以看出这个函数是ArrayList动态扩容的核心!!!
    }

接下来看扩容的核心源码,将涉及的源码都贴在下面,细心食用~

    /**
     * Copies the specified array, truncating or padding with nulls (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>null</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     * The resulting array is of exactly the same class as the original array.
     *
     * @param <T> the class of the objects in the array
     * @param original the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with nulls
     *     to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException if <tt>original</tt> is null
     * @since 1.6
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

    /**
     * Copies the specified array, truncating or padding with nulls (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>null</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     * The resulting array is of the class <tt>newType</tt>.
     *
     * @param <U> the class of the objects in the original array
     * @param <T> the class of the objects in the returned array
     * @param original the array to be copied
     * @param newLength the length of the copy to be returned
     * @param newType the class of the copy to be returned
     * @return a copy of the original array, truncated or padded with nulls
     *     to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException if <tt>original</tt> is null
     * @throws ArrayStoreException if an element copied from
     *     <tt>original</tt> is not of a runtime type that can be stored in
     *     an array of class <tt>newType</tt>
     * @since 1.6
     */
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));// 划重点 扩容底层实现System.arraycopy()
        return copy;
    }

虽然扩容的效率不差,但是也经不起频繁的调用,在创建的时候就要格外注意,给ArrayList赋予一个合适的大小,避免频繁扩容!

构造函数

既然说了创建,那我们对于我们使用需要了解的就是它的构造函数

(1)无参数构造

(2)指定大小:对于使用,最好使用这种,给予一个合适的大小,减少频繁的扩容,提升我们的代码性能

(3)接收一个Collection对象

“慢”说明

其实ArrayList因为是本身底层数组,在一些删除某个元素或者在中间添加一个元素,他就需要对数组进行拷贝,这种拷贝肯定是不如链式结构更换指向快,同时如果不给定一个合适的大小频繁扩容,可以再回头看看grow函数的源码。

使用小结

读完介绍就会很容易地总结出来,使用ArrayList的几个原则

(1)当需要频繁访问集合内的元素的时候,优先使用ArrayList

(2)给一个合适的大小,避免频繁扩容,也不至于影响性能

(3)当然如果数组已经足够满足你的需求,那就使用数组吧


奇妙的门木君
0 声望0 粉丝

Talk is cheap//KISS


下一篇 »
flink编译相关