ArrayList的初步讲解

一、概要

        一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,底层基于数组实现容量大小动态变化。

        默认初始容量:0,添加第一个元素后扩容为10。

        继承了 AbstractList 类,并实现了 List 接口。

二、源码分析

1、成员变量

//DEFAULT_CAPACITY为默认初始化大小
private static final int DEFAULT_CAPACITY = 10;
//EMPTY_ELEMENTDATA 用于空实例的共享空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//DEFAULTCAPACITY_EMPTY_ELEMENTDATA 用于默认大小的空实例的共享空数组实例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//elementData存储数据的地方
transient Object[] elementData;
//size是elementData中实际有多少个元素,
//注意elementData.length为集合容量,表示最多可以容纳多少个元素
private int size;
//继承自AbstractList,为的是提供一个快速失败的机制,如果集合中的元素个数发生变化,
//导致不明确的情况发生时,就会修改它,整个方法的调用进入快速失败模式
protected transient int modCount = 0;

2、常用方法

//集合增加数据。不需要扩容的情况下,时间复杂度为O(1),
//如果需要扩容的话,内部执行的 Arrays.copyOf() 方法,
//需要把原有数组中的元素复制到扩容后的新数组当中
public boolean add(E e);
//将新的元素插入到指定的位置。无论是否需要扩容,
//System.arraycopy() 肯定要执行,所以时间复杂度为O(n)
public boolean add(index,E);
//移除指定位置的数据。将指定位置上的元素删除,需要复制底层数组,所以时间复杂度为O(n)
public E remove(int index);
//清除集合数据
public void clear();
//获取集合中指定索引位置的数据。时间复杂度为O(1)
//因为是直接从底层数组根据下标获取的,和数组长度无关
public E get(int index);
//把指定数据放到指定的位置
public E set(int index, E element);
//添加一个集合中的所有数据    
public boolean addAll(Collection<? extends E> c);
//在数组中插入元素的时候,会把插入位置以后的元素依次往后复制,
//之后再通过elementData[index]=element将对应的下标的元素进行赋值,
//随后执行 size = s + 1,数组的长度发生变化    
System.arraycopy()
private void grow(int minCapacity)//处理数据

三、扩容机制

        使用add方法新增对象,

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

        会通过ensureCapacityInternal方法获取数组容器的容量,

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

        通过ensureExplicitCapacity方法判断数组的容量是否足够,

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

        不够则通过grow方法算出一个新的容量(新容量=旧容量 + (旧容量>>1)1.5倍),然后再根据新的容量创建出一个新的Object数组,最后通过Arrays.copyOf 方法将旧数组中的数据全部复制到新数组中去。

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);
    }

四、思考问题

1、为什么实现了Serializable接口却不想序列化elementData这个属性?

        transient变量不会贯穿对象的序列化和反序列化,生命周期仅存于调用者的内存中而不会写到磁盘里进行持久化。因为elementData数组的大小实际上是大于等于实际存储元素个数的,如果直接采用默认的序列化,会将很多null值序列化。

2、最重要的存储数据的地方,不序列化这些元素值怎么办?

        ArrayList中有一个private void writeObject(java.io.ObjectOutputStream s)的私有方法,把对象转成字节数据的输出到文件中保存,对象的输出过程称为序列化,可实现对象的持久存储。该方法是为了保证只序列化真实的元素。

private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        //输出元素计数和任何隐藏的内容
        int expectedModCount = modCount;
        s.defaultWriteObject();

        //将size作为与clone()行为兼容的容量输出出来
        s.writeInt(size);

        //按照正确的顺序写出所有的元素
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

3、ArrayList的增删过程是怎么样的?

        校验索引是否正确,检查数组容量,不够就去扩容,在增加删除指定位置的数据之后,会对整体的数据进行移动。

public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        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

        return oldValue;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值