ArrayList简单了解

本文主要分为两部分:

  • ArrayList动态修改长度的秘密、集合size和数组length的区别
  • ArrayList的数据结构(随机访问、插入、删除)

1. ArrayList动态修改长度的秘密、集合size和数组length的区别

//ArrayList构造函数的源码:(无参构造)
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    ...
    //数据容器
    transient Object[] elementData;

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

       public ArrayList() {
       //可以看出ArrayList其实是一个Object数组,初始化大小为0。
       //数组的特性:其容量固定无法修改;数组中无法判断其中实际
       //存有多少元素,length只是告诉我们数组的容量。
       //那么ArrayList是怎么实现动态修改长度又是怎么知道数据大小的?
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    ...
}
//既然动态变化长度,那么从添加和删除数据的方法来看吧。
//先看添加:
//ArrayList中有四个添加数据的方法:
//add(E e)添加数据到集合末尾;
//add(int index, E element)添加数据到指定位置;
//addAll(Collection<? extends E> c)添加集合到该集合末尾;
//addAll(int index, Collection<? extends E> c)添加集合到指定的位置。
//四个方法操作差不多,只需要了解其中一个就可以了,
//看一下ArrayList add(E e)方法的源码:

private static final int DEFAULT_CAPACITY = 10;

private static final int MAX_ARRAY_SIZE = 
Integer.MAX_VALUE - 8;

private int size;

    public boolean add(E e) {
        //可以看出是在这个方法中确保了容器的大小,
        //也就是动态改变容器大小
        ensureCapacityInternal(size + 1); 
        //添加数据到尾部,在这里size++;
        elementData[size++] = e;
        return true;
    }

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //确认容器大小,默认最小值为10。
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        //记录修改次数
        modCount++;
        //判断是否需要扩充数组长度
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }


private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        //容量增长一半
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //下面这个判断一般忽略
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //到这就能看出动态增长实际上是new了一个新的数组,
        //然后将原数组数据复制到新数组中,
        //然后elementData指向新的数组。
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0)
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
//最后再返回看add()方法中,elementData[size++] = e;
//将数据赋值到了末尾,然后size+1。

//在ArrayList中数组容量最小为10,在达到临界值后,增长原数组长度的
//一半大小。size初始化为0,size+=添加的元素个数。
//集合size和数组length的区别:size<=length。
//常用的remove方法有:
//remove(Object o):删除指定的元素;
//remove(int index):删除指定下标的元素:
//removeAll(Collection<?> c):删除集合中的元素。
//主要看一下后两个删除方法吧,第一个第二个内部操作差不多。

public E remove(int index) {
        //角标越界
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        //修改次数
        modCount++;
        E oldValue = (E) elementData[index];
        //要复制的长度
        int numMoved = size - index - 1;
        if (numMoved > 0){
            //arraycopy(Object src,  int  srcPos,
            //          Object dest, int destPos,
            //          int length);
            //src:源数组;srcPos:源数组要复制的起始位置;
            //dest:目的数组;destPos:数据放置目的数组的起始位置;
            //length:要复制的长度。
            //如:int[] ints={0,1,2,3,4,5,6};
            //arraycopy(ints,0,ints,3,2);
            //output:{0,1,2,0,1,5,6}
            System.arraycopy(
            elementData, index+1, 
            elementData, index,
            numMoved);
                             }
         //数据长度减一,最后一个数据制空。
         //在这里size--
        elementData[--size] = null; 

        return oldValue;
    }


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

private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                //去掉要删除的,然后整理数组
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            //为了兼容 c.contains()的异常。
            if (r != size) {
                //复制异常以后的数据到数组中
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
                //制空重组后的数组中不需要的部分。
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;//在这里size等于重组后的数据长度
                modified = true;
            }
        }
        return modified;
    }
//可以看出,在remove的方法中没有动态修改数组的长度。
//size-=删除的元素个数。

2. ArrayList的数据结构(插入、删除、随机访问)

//ArrayList基于顺序存储结构,适合随机访问元素,
//不适合在List中大量插入和移除元素。
public void add(int index, E element) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        ensureCapacityInternal(size + 1);
        System.arraycopy(elementData, index, 
                         elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
//插入方法中需要将index以后的数据元素往后移动,
//当数据量大的时候,这种插入操作无疑是耗时的。


public E remove(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        modCount++;
        E oldValue = (E) elementData[index];
        int numMoved = size - index - 1;
        if (numMoved > 0){

            System.arraycopy(
            elementData, index+1, 
            elementData, index,
            numMoved);
                             }
        elementData[--size] = null; 
        return oldValue;
    }
//删除操作需要将index以后的数据元素往后移动,
//和插入一样,当数据量大的时候,这种操作是比较耗时的。


public E get(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        return (E) elementData[index];
    }
//随机访问get方法通过数组下标来返回数据。基于顺序存储结构,
//为一连串的数据存储,通过下标可以直接返回数组中数据。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值