详解ArrayList底层源码

前提:以ArrayList arrayList = new ArrayList<>()为例;

1.ArrayList构造器

// 1)无参构造器:底层数组没有申请空间
public ArrayList() {
    // 给 ArrayList 的底层数组开辟了默认大小的空间【0个】【重点】
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    // 开辟了内存空间, 但数组长度为 0
    // private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
}


// 2)构造数字: 给底层数组进行申请 initialCapacity 个空间的数组
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        // 给 elementData 申请 initialCapacit 个空间
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        // initialCapacit 为 0,底层没有开辟空间,和默认初始化一样
        this.elementData = EMPTY_ELEMENTDATA;
        // private static final Object[] EMPTY_ELEMENTDATA = {};
    } else {
        // initialCapacity 小于 0,则直接抛出异常
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    }
}

// 3) 构造 Collection: 只要是 Collection 下的实现类都可以被 ArrayList 构造
// ? extends E: 只要是 E 的子类及 E 类型的类都可以使用
public ArrayList(Collection<? extends E> c) {
    // 把 c 转化为数组,并使 elementData 数组指向c转化后的数组
    elementData = c.toArray();

    // 判断 elementData 的长度是否为 0
    if ((size = elementData.length) != 0) {
        // 检查 c 转化后的数组是否是 Object[] 类型,不是的话全部转化为 Object[] 类型
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // 数组长度为 0 的话,赋给 elementData 空数组
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

2.打印数组元素

public String toString() {
    // 迭代器
    Iterator<E> it = iterator();
    // 调用对象中无元素
    if (! it.hasNext())
        return "[]";

    // 利用 StringBuilder 进行打印数组信息
    StringBuilder sb = new StringBuilder();
    // append 方法连接内容
    sb.append('[');
    // 死循环
    for (;;) {
        // E 是类型, e 获取当前调用对象中的值
        E e = it.next();
        // 若 e 和当前调用对象地址相同, 则添加字符串 this Collection, 反之,把e的内容添加到 sb 中
        sb.append(e == this ? "(this Collection)" : e);
        // 没有下一个元素了, 直接转化为字符串并返回
        if (! it.hasNext())
            return sb.append(']').toString();
        // 添加逗号和空格
        sb.append(',').append(' ');
    }
}

// 迭代器对应方法
public abstract Iterator<E> iterator(); //对应的抽象方法

// 子类中实现父类中的抽象方法
// 注:谁调用 toString 方法, iterator 就获取谁的内容
public Iterator<E> iterator() {
    // 返回迭代器对象
    return new Itr();
}

3.添加元素

  • add(E e):添加到ArrayList的最后
// 添加元素
public boolean add(E e) {
    // 判断底层数组是否需要扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 把元素添加到最后
    elementData[size++] = e;
    return true;
}

// 扩容
private void ensureCapacityInternal(int minCapacity) {
    // 计算容量和确定扩容
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// 计算容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 若 elementData 数组是默认空元素数组
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 返回 private static final int DEFAULT_CAPACITY = 10;
        // 返回默认的 10 个空间的数组大小
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    // 不是默认空元素数组,直接返回 minCapacity
    return minCapacity;
}

// 确定扩容
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // 若 minCapacity 比底层 elementData 数组长度大,则进行扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity); // 需要扩容
}


// 真正扩容的地方
private void grow(int minCapacity) {
    // oldCapacity 为原数组的长度
    int oldCapacity = elementData.length;
    // newCapacity 为原数组长度的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    
    // minCapacity 的值大于 newCapacity 的值,使用 newCapacity 作为数组长度
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
        
    // 若 newCapacity 比 MAX_ARRAY_SIZE【Integer.MAX_VALUE - 8】还大
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    
    // 使用 Arrays.copyOf 进行扩容
    elementData = Arrays.copyOf(elementData, newCapacity);
}

// 当底层数组长度比 MAX_ARRAY_SIZE【Integer.MAX_VALUE - 8】还大
private static int hugeCapacity(int minCapacity) {
    // 给定的长度小于 0, 抛出异常
    if (minCapacity < 0) 
        throw new OutOfMemoryError();//抛出异常
    // 若 minCapacity 大于 MAX_ARRAY_SIZE,使用整型最大值作为数组长度
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

重点:

  1. 若使用无参构造器初始化底层数组的话,默认是开辟10个大下的数组空间【添加元素的时候开辟】
    private static final int DEFAULT_CAPACITY = 10;
  2. 添加元素若底层elementData数组满了,按照1.5倍进行扩容
    int newCapacity = oldCapacity + (oldCapacity >> 1);
  3. 底层的扩容是使用Arrays.copyOf方法进行扩容的
    elementData = Arrays.copyOf(elementData, newCapacity);
  4. 无参构造器中的数组赋的数组是默认空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  5. 有参构造器中的数组赋值若赋为0, 那么赋的是空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
  • add(int index, E element):在任意位置添加元素
// 在 index 处的位置添加元素
public void add(int index, E element) {
    // 检查下标是否合法
    rangeCheckForAdd(index);

    // 这个地方一样的需要扩容检查,上面代码已经说明
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 在 elementData 数组的 index 位置拷贝到 elementData 数组的 index+1 的位置,拷贝到数组后面
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    // 插入要添加的元素
    elementData[index] = element;
    // elementData 数组长度自增
    size++;
}

// 判断下标是否合法
private void rangeCheckForAdd(int index) {
    // 若 index 比数组长度大或 index 小于 0,则抛出异常
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

// System.arraycopy 方法
public static native void arraycopy(Object src,  int  srcPos,
                                    Object dest, int destPos,
                                    int length);

// 打印异常信息
private String outOfBoundsMsg(int index) {
    return "Index: "+index+", Size: "+size;
}

4.更新元素和获取元素

  • set(int index, E element):更新elementData中index处的元素值
public E set(int index, E element) {
    // 判断 index 的下标是否合法
    rangeCheck(index);
    
    // 把原来的值赋给 oldValue
    E oldValue = elementData(index);
    // 更新 index 处的值
    elementData[index] = element;
    // 返回原来的值
    return oldValue;
}

// 判断 index 处的合法性
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

// 打印异常信息
private String outOfBoundsMsg(int index) {
    return "Index: "+index+", Size: "+size;
}
  • get(int index):获取index处的元素
public E get(int index) {
    // 判断 index 的下标是否合法
    rangeCheck(index);
    
    // 直接返回 index 处的元素值
    return elementData(index);
}

// 判断 index 处的合法性
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

// 打印异常信息
private String outOfBoundsMsg(int index) {
    return "Index: "+index+", Size: "+size;
}

5.元素的出现位置

  • indexOf(Object o):元素第一次出现的位置
public int indexOf(Object o) {
    // 若元素为 null
    if (o == null) {
        // 查找 null 第一次出现的下标【0下标开始查找】
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        // 查找元素 o 第一次出现的下标【0下标开始查找】
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    // 没有找到
    return -1;
}
  • lastIndexOf(Object o):元素最后一次出现的位置
public int lastIndexOf(Object o) {
    // 若元素为 null
    if (o == null) {
        // 查找 null 元素最后一次出现的位置【从最后一个有效下标查找】
        for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } else {
        // 查找 o 元素最后一次出现的位置【从最后一个有效下标查找】
        for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    // 没有找到
    return -1;
}

6.删除元素

  • remove(int index):按照下标进行删除
public E remove(int index) {
    // 检查下标的合法性
    rangeCheck(index);

    // 修改次数
    modCount++;
    // 保存要删除的值
    E oldValue = elementData(index);
    // 移动的元素个数
    int numMoved = size - index - 1;
    // 说明不是最后一个元素
    if (numMoved > 0)
        // 把 index + 1 的位置拷贝到 index 的位置
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    // 最后一个元素直接置为 null 即可
    elementData[--size] = null; // clear to let GC do its work
    // 返回删除的值
    return oldValue;
}

// 判断 index 处的合法性
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

// 打印异常信息
private String outOfBoundsMsg(int index) {
    return "Index: "+index+", Size: "+size;
}
  • remove(Object o):按照元素值进行删除
public boolean remove(Object o) {
    // 若元素值为 null
    if (o == null) {
        for (int index = 0; index < size; index++)
            // 判断是否和 null 相等
            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;
}

// 进行删除的地方
private void fastRemove(int index) {
    modCount++;
    // 移动元素个数
    int numMoved = size - index - 1;
    // 说明删除的不是最后一个元素
    if (numMoved > 0)
        // 把 index+1 - 数组长度的值 拷贝到 index - 数组长度的值【即前移】
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    // 删除的是最后一个元素
    elementData[--size] = null; // clear to let GC do its work
}

7.查找元素是否存在、顺序表长度

  • contains(Object o):查找元素是否存在顺序表中
public boolean contains(Object o) {
    // 判断返回的下标是否大于 0
    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;
}
  • size():顺序表的长度
public int size() {
    // 直接返回顺序表长度
    return size;
}

8.判断顺序表是否非空和清空顺序表

  • isEmpty():判断顺序表是否为空
public boolean isEmpty() {
    // 判断顺序表长度,若为 0,则为空
    return size == 0;
}
  • clear():清空顺序表
public void clear() {
    modCount++;

    // clear to let GC do its work
    // 若为引用类型,则把每个引用类型置为 null
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    // 最后把顺序表长度置为0
    size = 0;
}

9.ArrayList的遍历方式

  • 简单for循环
public class ArrayListMethod {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        arrayList.add(4);
        arrayList.add(5);
        arrayList.add(6);

        for (int i = 0; i < arrayList.size(); i++) {
            System.out.print(arrayList.get(i) + " ");
        }
    }
}
  • for - each循环
public class ArrayListMethod {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        arrayList.add(4);
        arrayList.add(5);
        arrayList.add(6);

        // for - each 循环
        for (Integer ret : arrayList) {
            System.out.print(ret + " ");
        }
    }
}
  • 迭代器
public class ArrayListMethod {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        arrayList.add(4);
        arrayList.add(5);
        arrayList.add(6);

        // 迭代器遍历
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            Object ret = iterator.next();
            System.out.print(ret + " ");
        }
    }
}

10.ArrayList的缺点与优点

  • 添加, 删除元素效率较低
  • 扩容的时候需要消耗不少空间, 若后续不使用, 造成浪费
  •  ArrayList可以通过下标进行随机访问和获取元素
  • 顺序表适合静态的数据进行查找和更新
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值