ArrayList及扩容机制-源码讲解

ArrayList 是 Java 集合框架中的一个重要类,它实现了 List 接口,并提供了动态数组的功能。ArrayList 的大小可以随着元素的添加或删除而动态变化,无需手动管理内存

  • 什么是集合

    存储空间可变的存储模型,允许存储的数据容量动态变化。

  • ArrayList集合的特点

    长度可以变化,只能存储引用数据类型。

  • 泛型的使用

    用于约束集合中存储元素的数据类型

创建ArrayList

语法

//创建一个空的集合对象 
ArrayList<Object> list = new ArrayList<>();
  • < >只能填写引用数据类型:Integer,Double,String...(不能写int,double)
  • 如果没有指定初始容量,默认初始容量为 10。

常用语法

import java.util.ArrayList;

public class ArrayListExample {

    public static void main(String[] args) {
        // 创建一个存储姓名的 ArrayList
        ArrayList<String> names = new ArrayList<>();

        // 添加元素
        names.add("张三");
        names.add("李四");
        names.add("王五");

        // 打印 ArrayList
        System.out.println("当前 ArrayList:" + names); // 输出:[张三, 李四, 王五]

        // 获取元素
        String name = names.get(1); // 获取第二个元素
        System.out.println("第二个元素:" + name); // 输出:李四

        // 修改元素
        names.set(1, "赵六"); // 将第二个元素修改为 "赵六"
        System.out.println("修改后的 ArrayList:" + names); // 输出:[张三, 赵六, 王五]

        // 删除元素
        names.remove("赵六"); // 删除第一个 "赵六"
        System.out.println("删除元素后的 ArrayList:" + names); // 输出:[张三, 王五]

        // 检查 ArrayList 是否为空
        System.out.println("ArrayList 是否为空:" + names.isEmpty()); // 输出:false

        // 清空 ArrayList
        names.clear();
        System.out.println("清空后的 ArrayList:" + names); // 输出:[]

        // 添加元素
        names.add("张三");
        names.add("李四");

        // 将 ArrayList 转换为数组
        String[] array = names.toArray(new String[0]);
        System.out.println("数组的第一个元素:" + array[0]); // 输出:张三
    }
}
方法描述示例
add(E e)将元素添加到 ArrayList 末尾names.add("张三");
add(int index, E element)在指定索引处添加元素names.add(1, "李四");
remove(int index)删除指定索引处的元素names.remove(1);
remove(Object o)删除第一个匹配的元素names.remove("张三");
get(int index)获取指定索引处的元素String name = names.get(1);
set(int index, E element)修改指定索引处的元素names.set(1, "王五");
size()返回 ArrayList 的大小(元素数量)int size = names.size();
isEmpty()判断 ArrayList 是否为空boolean isEmpty = names.isEmpty();
contains(Object o)判断 ArrayList 是否包含指定元素boolean contains = names.contains("张三");
clear()清空 ArrayListnames.clear();
toArray()将 ArrayList 转换为数组String[] array = names.toArray(new String[0]);

 

 优缺点

优点

  1. 动态大小ArrayList 可以根据需要动态调整其大小,不需要在创建时指定固定的大小。
  2. 快速随机访问:提供 O(1) 时间复杂度的随机访问,因为它基于数组实现,可以通过索引直接访问元素。
  3. 有序集合:保持元素的插入顺序,允许按照添加元素的顺序遍历列表。
  4. 允许重复:可以存储重复的元素,适用于需要重复元素的场景。
  5. 丰富的 API:提供了丰富的操作方法,如添加、删除、修改、查询等,使用方便。

缺点

  1. 插入和删除性能:在中间位置插入和删除元素的时间复杂度是 O(n),因为需要移动元素以保持列表的连续性。
  2. 内存浪费:由于需要动态扩容,可能会预分配比实际需要更多的内存,从而导致内存浪费。
  3. 非线程安全ArrayList 不是线程安全的,在多线程环境中使用时需要手动同步或使用同步的 ArrayList
  4. 扩容开销:当容量不足时,需要扩容,这个过程涉及到创建一个更大的数组并将旧数组的内容复制到新数组中,时间复杂度为 O(n)。

适用场景

  • 频繁读取和遍历操作
  • 需要保持元素插入顺序
  • 允许重复元素

不适用场景

  • 频繁的插入和删除操作,特别是在中间位置
  • 需要线程安全的环境

扩容机制

当 ArrayList 中的元素数量超过其当前容量时,ArrayList 会自动扩容,以便能够容纳更多的元素。扩容的具体步骤如下:

  1. 检查容量:在向 ArrayList 添加新元素时,首先会检查当前容量是否足够。如果容量不足,则需要扩容。
  2. 计算新容量:新容量通常是旧容量的 1.5 倍。具体来说,新的容量计算公式为 newCapacity = oldCapacity + (oldCapacity >> 1)。例如,如果当前容量是 10,那么新的容量将是 10 + (10 >> 1) = 10 + 5 = 15
  3. 创建新数组:根据计算出的新容量,创建一个新的数组。
  4. 复制旧数据:将旧数组中的数据复制到新数组中。
  5. 替换旧数组:用新数组替换旧数组,完成扩容。
  6. 回收旧数组: 旧数组不再被使用,会被 Java 垃圾回收机制自动回收。

源码分析

1.add

首先看一下 add 方法,这是向 ArrayList 添加元素的入口:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 确保容量足够
    elementData[size++] = e;
    return true;
}

add 方法:当调用 add 方法时,首先调用 ensureCapacityInternal 方法来确保 ArrayList 有足够的容量来容纳新元素。

2.ensureCapacityInternal

ensureCapacityInternal 方法是确保 ArrayList 有足够的容量来容纳新元素的关键:

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

ensureCapacityInternal 方法:这个方法首先调用 calculateCapacity 方法来计算所需的最小容量。如果当前的 elementData 数组是默认空数组(即初始容量),则返回默认容量和所需容量中的较大值。

3.ensureExplicitCapacity

ensureExplicitCapacity 方法是真正执行扩容操作的地方:

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

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

ensureExplicitCapacity 方法:这个方法增加了 modCount(用于快速失败机制),并检查所需的最小容量是否超过当前数组的长度。如果超过,则调用 grow 方法进行扩容。

4.grow

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

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

grow 方法:这个方法是扩容的核心。它首先计算新的容量,通常是旧容量的 1.5 倍。如果新的容量仍然不足以满足所需的最小容量,则将新的容量设置为所需的最小容量。如果新的容量超过了最大数组大小,则调用 hugeCapacity 方法来处理极端情况。最后,使用 Arrays.copyOf 方法将旧数组的数据复制到新数组中。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值