Arraylist源码解析(JDK1.8)

最近在阅读容器的源码,因此也想找个地方总结一下分析成果,抛砖引玉,如有错误,还望指正。

1. 简述

ArrayList是一个实现list接口,长度可变的,非同步的、可以允许null元素的数组。
该类所有的操作耗费的时间都是线性的,O(n)。

因为所有的操作都是非同步的,因此多线程的情况下,增加元素、包括自动的扩容都是会有安全问题,为了解决线程安全问题,jdk也提供了相应的工具
List list = Collections.synchronizedList(new ArrayList(...));

2. 类的继承关系

首先看一下该类主要的继承和实现关系:
在这里插入图片描述
粗虚线表示抽象类,细虚线表示接口。

3. 相关字段

  1. 默认容器大小
    private static final int DEFAULT_CAPACITY = 10

  2. 空数组的实例
    private static final Object[] EMPTY_ELEMENTDATA = {}

  3. 默认大小的空数组实例,与EMPTY_ELEMENTDATA不同,在添加原数组时会自动扩容
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

  4. 用于存放数据,当空数组中存放了一个数组,则容器的默认大小则置为10
    transient Object[] elementData;

  5. 数组中实际存放数据的大小
    private int size;

4. 构造方法

创建一个指定大小的list
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

创造一个默认大小的容器。容器默认大小为10

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

可以在构造器中默认传入一个集合

 /**
 * 1. 将传入的集合转为数组
 * 2. 如果该数组大小不为0,则将原数组拷贝到Object数组中,
 * 3. 如果数组大小为0,则直接创建一个空数组
 * @param c
 */
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

5. 常见方法讲解

1. add、addAll

  1. 将元素添加到数组的尾部
/**
 * 1. 在真正的添加元素之前,首先确定是否需要添加扩容
 * 2. 添加元素
 * @param e
 * @return
 */
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
  1. 将元素查到数组的指定位置
/**
 * 1. 确认下标合法
 * 2. 检查是否需要扩容
 * 3. 将index开始的数组整体往后移
 * 4. 插入真正的元素
 * @param index
 * @param element
 */
public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
            size - index);
    elementData[index] = element;
    size++;
}
  1. 将集合c添加到原list后面
/**
  * 将集合c追加到原数组的末尾,内部的原理是通过复制的方式实现。
  * @param c
  * @return
  */
 public boolean addAll(Collection<? extends E> c) {
     Object[] a = c.toArray();
     int numNew = a.length;
     ensureCapacityInternal(size + numNew);  // Increments modCount
     System.arraycopy(a, 0, elementData, size, numNew);
     size += numNew;
     return numNew != 0;
 }

2. remove/clear

  1. 删除指定位置的元素
/**
 * 1. 检查下标是否合法
 * 2. 获取被删除的元素
 * 3. 通过arraycopy复制方式删除元素
 * 4. 释放最末尾的元素,节约内存
 * @param index
 * @return
 */
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;
}
  1. 删除指定的元素
/**
 * 目的:删除列表中第一次出现o的对象
 * 1. 判断o是否为null,如果为null,则通过循环删除null
 * 2. 不为空,则删除对应的下标。
 * @param o
 * @return
 */
public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}
  1. 删除所有的元素
/**
  * 删除数组中所有的元素
  * 具体实现:将每个下标对应的数据设置为null
  */
public void clear() {
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}
  1. 删除指定范围的元素
/**
 * 指定删除(fromIndex,toIndex)的数据
 * @param fromIndex
 * @param toIndex
 */
protected void removeRange(int fromIndex, int toIndex) {
    modCount++;
    int numMoved = size - toIndex;
    System.arraycopy(elementData, toIndex, elementData, fromIndex,
            numMoved);

    // clear to let GC do its work
    int newSize = size - (toIndex-fromIndex);
    for (int i = newSize; i < size; i++) {
        elementData[i] = null;
    }
    size = newSize;
}

3. 扩容机制

可以在前面的添加元素中看到,每次添加元素之前,必须检查list是否有足够的容量容纳对象。如果不够,则需要扩容。

  1. 保证数组最小容量
  2. 计算容器大小
  3. 确定扩容逻辑
  4. 具体的扩容操作
/**
 * 保证数组最小容量
 * 1. 计算容量
 * @param minCapacity
 */
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
 * 计算容器大小
 * 1. 判断现存数据默认数组数据是否相同,如果相同,则在默认的大小和minCapacity取最大值
 * 2. 如果不相同,则直接返回minCapacity
 * @param elementData
 * @param minCapacity
 * @return
 */
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
/**
  * 确认是否需要扩充容量
  * modCount为父类的定义的元素,用于统计扩容的次数,当真正数组的最小长度大于数组容量,则需要扩容。
  * @param minCapacity
  */
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
/**
 * 扩充数组的容量,确保数组最少能够容纳minCapacity个元素
 * 1. 确定旧list的容量
 * 2. 每次都两倍的扩容。比如初始大小为10,第二次扩容则为10+10/5 = 15,每次扩容都是上次的1.5倍
 * 3. 通过Arrays.copyOf()方式扩容
 * @param minCapacity the desired minimum capacity
 */
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);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

注意扩容逻辑中的hugeCapacity()函数,其逻辑主要是:

/**
 * 扩充最大容量
 * @param minCapacity
 * @return
 */
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

4. 查找数组大小

/**
  * 返回数组的大小
  * @return
  */
 public int size() {
     return size;
 }

5. 判断是否为空

/**
  * 判断list是否为空
  * @return
  */
public boolean isEmpty() {
    return size == 0;
}

6. 是否包含某个元素

具体的是通过indexOf()方法寻找对象o的下标。

/**
  * 是否包含对象o。如果包含,则返回true;不包含,返回false
  * @param o 
  * @return
  */
public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

查找对象o的下标

/**
  * 返回第一个等于o对象的下标。如果不包含,则返回-1
  * @param o
  * @return int
  */
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;
}

7. 将list转化为数组

public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

8. 获取下标index的元素

/**
  * 返回下标为index的元素
  * 1. 判断下标是否合理
  * 2. 通过elementData()获取对应的值
  * @param index
  * @return
  */
public E get(int index) {
    rangeCheck(index);
    return elementData(index);
}

通过elementData获取对应的元素

E elementData(int index) {
    return (E) elementData[index];
}

9. 替换元素

/*
 * 代替下标为index的元素,返回被代替的元素
 * @param index
 * @param element
 * @return
 */
public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

总结

  1. 在ArrayList中,无论是add、删除元素还是扩容,都大量用到了System.arraycopy方式进行数组复制,因此针对于插入、删除操作及扩容操作整体性能较慢。当然这里说的慢指的平均时间复杂度
  2. ArrayList和Vector内部的实现逻辑基本一致,不同的是Vector的所有操作都是加了synchronize,都是同步方法,Vector性能整体性能较差。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值