大家天天在用List,ArrayList一般来讲应该是程序员用的最多的汇合类了。

咱们明天钻研一下ArrayList。

总体来讲,从底层数据结构或者源码的角度看,List比Map或者Set要简略。

底层数据结构

ArryList其实就是可变长数组。

初始化的时候,能够指定容量,不指定容量的话,ArrayList被初始化为空数组,首次存入数据的时候才进行容量初始化,初始化最小容量为10。

ArrayList的容量管制

每次存入数据到ArrayList的时候,首先查看容量:

private void ensureExplicitCapacity(int minCapacity) {        modCount++;        // overflow-conscious code        if (minCapacity - elementData.length > 0)            grow(minCapacity);    }

如果ArrayList容量不能满足本次数据存储的要求的话,调用grow办法扩容:

private void grow(int minCapacity) {        // overflow-conscious code        int oldCapacity = elementData.length;        int newCapacity = oldCapacity + (oldCapacity >> 1);        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);    }

grow办法首先查看原容量扩容1.5倍是否能满足要求,能满足则扩容到原来的1.5倍,否则扩容到能满足数据存入要求的容量。

扩容后调用Arrays.copyOf(elementData, newCapacity)把旧的数据copy到扩容后的新数组。Arrays.copyof办法其实也是调用System.arraycopy办法实现copy。

数据存入

调用add(E e)办法在ArrayList尾部追加数据。
调用addAll(Collection<? extends E> c)在ArrayList尾部追加汇合c中的所有数据。

ArrayList反对追加数据到其指定地位(通过调用办法add(int index, E element)),底层通过调用System.arraycopy办法实现。

钻研ArrayList源码会发现其中大量应用System.arraycopy办法,所以,尽管ArrayList其实就是数组操作,然而性能也不至于太差。

获取数据

应用ArrayList的时候咱们个别会有两种获取数据的需要,一种就是获取指定地位的数据,通过get(int index)办法获取,因为ArrayList其实就是数组,所以,获取指定地位的数据效率十分高,工夫复杂度为O(1)。

另外一种就是判断是否蕴含某一数据,调用contains(Object o)办法:

public boolean contains(Object o) {        return indexOf(o) >= 0;    }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;    }

其实猜都能猜到他必须要遍历数组了,所以效率不会太高。

其余

读源码的目标除了从底层彻底理解其工作原理之外,还有一个重要的目标就是学习最最NB的JAVA程序员的编码思维和习惯。

比方开释内存:

   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    }

比方代码重用:

    public boolean retainAll(Collection<?> c) {        Objects.requireNonNull(c);        return batchRemove(c, true);    }    public boolean removeAll(Collection<?> c) {        Objects.requireNonNull(c);        return batchRemove(c, false);    }

retainAll是只保留参数汇合中的数据,removeAll则正好相同,移除参数汇合中的数据,两个办法目标正好相同,则通过调用batchRemove、传递complement为true或false达到目标。

值得每个尤其是老手程序员认真学习。

晚安!