杰瑞科技汇

ArrayList 如何实现动态扩容机制?

什么是 ArrayList?

ArrayList 是 Java 集合框架中 List 接口的一个可调整大小的数组实现,它就是一个可以动态改变长度的数组。

ArrayList 如何实现动态扩容机制?-图1
(图片来源网络,侵删)

与 Java 中固定长度的原生数组(int[], String[] 等)相比,ArrayList 提供了更灵活、更强大的功能,是开发中处理动态数据集合的首选。

核心特点:

  • 有序性ArrayList 中的元素会按照插入的顺序进行存储(也称为“插入顺序”)。
  • 允许重复:可以存储多个重复的元素。
  • 允许 null 元素:可以存储 null 值。
  • 非线程安全:在多线程环境下,如果多个线程同时修改一个 ArrayList,可能会导致数据不一致,如果需要线程安全,可以使用 Collections.synchronizedList(new ArrayList<>())CopyOnWriteArrayList
  • 基于数组:它的底层实现是一个动态数组,这使得它在随机访问(通过索引)元素时非常快,时间复杂度为 O(1)。

如何创建和初始化 ArrayList?

创建 ArrayList 通常有两种方式:指定初始容量和不指定。

1 基本语法

// 1. 创建一个空的 ArrayList,默认初始容量是 10
ArrayList<String> list1 = new ArrayList<>();
// 2. 创建一个指定初始容量的 ArrayList
//   如果大概知道要存储多少元素,指定初始容量可以提高性能,避免多次扩容
ArrayList<Integer> list2 = new ArrayList<>(20);
// 3. 使用 Java 7 的 "菱形语法" (Diamond Syntax)
//   编译器会自动推断泛型类型
ArrayList<Double> list3 = new ArrayList<>();
// 4. 从一个已有的集合创建 ArrayList
//   从一个数组创建
String[] array = {"A", "B", "C"};
ArrayList<String> list4 = new ArrayList<>(Arrays.asList(array));

2 注意事项:泛型

ArrayList<String> 中的 <String> 就是泛型,它规定了这个 ArrayList 只能存储 String 类型的对象,这提供了类型安全,避免了在运行时出现 ClassCastException

ArrayList 如何实现动态扩容机制?-图2
(图片来源网络,侵删)
  • ArrayList<String>:只能存 String
  • ArrayList<Integer>:只能存 Integer(注意,不能存基本类型 int,要使用其包装类)。
  • ArrayList<Object>:可以存任何类型的对象。

如果不使用泛型,ArrayList 默认可以存储任何 Object 类型的对象,但非常不推荐,因为它失去了编译期的类型检查。

// 不推荐的方式,可能导致运行时错误
ArrayList list = new ArrayList();
list.add("Hello");
list.add(123);
String str = (String) list.get(0); // 需要强制转换,且容易出错

常用方法

以下是 ArrayList 最常用的一些方法,并附有示例。

1 添加元素

方法 描述 示例
add(E e) 在列表的末尾添加一个元素。 list.add("Apple");
add(int index, E e) 在列表的指定位置插入一个元素。 list.add(0, "Banana"); // 插入到开头
ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");       // 添加 "Apple"
fruits.add("Orange");      // 添加 "Orange"
fruits.add(1, "Banana");   // 在索引 1 的位置插入 "Banana"
// fruits 现在是: ["Apple", "Banana", "Orange"]

2 访问元素

方法 描述 示例
get(int index) 返回列表中指定位置的元素。 String fruit = fruits.get(0);
size() 返回列表中的元素数量(即长度)。 int count = fruits.size();
ArrayList<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Orange"));
String firstFruit = fruits.get(0); // firstFruit 是 "Apple"
int size = fruits.size();          // size 是 3

3 修改元素

方法 描述 示例
set(int index, E e) 用指定的元素替换列表中指定位置的元素。 fruits.set(0, "Grape");
ArrayList<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Orange"));
fruits.set(0, "Grape"); // 将索引 0 的 "Apple" 替换为 "Grape"
// fruits 现在是: ["Grape", "Banana", "Orange"]

4 删除元素

方法 描述 示例
remove(int index) 移除列表中指定位置的元素。 fruits.remove(0); // 移除第一个元素
remove(Object o) 移除列表中第一次出现的指定元素。 fruits.remove("Banana");
ArrayList<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Orange", "Banana"));
// 按索引删除
fruits.remove(0); // 移除 "Apple"
// fruits 现在是: ["Banana", "Orange", "Banana"]
// 按对象删除
fruits.remove("Banana"); // 移除第一个 "Banana"
// fruits 现在是: ["Orange", "Banana"]

5 遍历 ArrayList

遍历是操作集合最常见的操作,有三种主要方式:

  1. 使用 for 循环(通过索引)

    ArrayList 如何实现动态扩容机制?-图3
    (图片来源网络,侵删)
    for (int i = 0; i < fruits.size(); i++) {
        System.out.println(fruits.get(i));
    }
  2. 使用 for-each 循环(增强型 for 循环) - 最推荐 这种方式代码更简洁,并且避免了索引越界的风险。

    for (String fruit : fruits) {
        System.out.println(fruit);
    }
  3. 使用迭代器 迭代器提供了一种更安全的方式来遍历和修改集合,特别是在遍历过程中需要删除元素时。

    Iterator<String> iterator = fruits.iterator();
    while (iterator.hasNext()) {
        String fruit = iterator.next();
        System.out.println(fruit);
        // 如果需要删除元素,必须使用迭代器的 remove 方法
        // iterator.remove();
    }

6 其他实用方法

方法 描述 示例
contains(Object o) 如果列表包含指定的元素,则返回 true boolean hasApple = fruits.contains("Apple");
isEmpty() 如果列表不包含元素,则返回 true boolean isEmpty = fruits.isEmpty();
clear() 移除列表中的所有元素。 fruits.clear();
toArray() 将列表转换为一个数组。 Object[] array = fruits.toArray();

ArrayList vs. 原生数组

特性 ArrayList 原生数组 (T[])
长度 可动态调整,自动处理扩容和缩容。 固定长度,创建后不能改变。
性能 随机访问快 (O(1)),在中间/头部插入/删除慢 (O(n)),因为需要移动元素。 随机访问极快 (O(1)),插入/删除慢 (O(n))。
类型安全 通过泛型在编译期提供类型安全。 通过声明类型(如 String[])提供类型安全。
功能 提供丰富的 API(add, remove, size, contains 等)。 功能非常基础,只有 length 属性和 [] 访问。
使用场景 当元素数量不确定,需要频繁增删改查时。 当元素数量固定,且对性能要求极高(尤其是内存)时。

示例代码

下面是一个完整的示例,展示了 ArrayList 的基本用法:

import java.util.ArrayList;
import java.util.Arrays;
public class ArrayListExample {
    public static void main(String[] args) {
        // 1. 创建 ArrayList
        ArrayList<String> students = new ArrayList<>();
        // 2. 添加元素
        System.out.println("添加元素...");
        students.add("张三");
        students.add("李四");
        students.add("王五");
        System.out.println("当前学生列表: " + students); // 自动调用 toString() 方法
        // 3. 在指定位置插入
        students.add(1, "赵六");
        System.out.println("在索引1处插入'赵六'后: " + students);
        // 4. 访问元素
        System.out.println("\n访问元素...");
        String firstStudent = students.get(0);
        System.out.println("第一个学生是: " + firstStudent);
        System.out.println("学生总数: " + students.size());
        // 5. 修改元素
        System.out.println("\n修改元素...");
        students.set(0, "张三丰");
        System.out.println("将第一个学生修改为'张三丰'后: " + students);
        // 6. 删除元素
        System.out.println("\n删除元素...");
        students.remove("赵六"); // 按对象删除
        System.out.println("删除'赵六'后: " + students);
        students.remove(0); // 按索引删除
        System.out.println("删除第一个学生后: " + students);
        // 7. 遍历 ArrayList
        System.out.println("\n遍历学生列表:");
        for (String student : students) {
            System.out.println("- " + student);
        }
        // 8. 检查元素是否存在
        System.out.println("\n检查元素是否存在...");
        boolean hasLiSi = students.contains("李四");
        System.out.println("列表中是否包含'李四'? " + hasLiSi);
        // 9. 清空列表
        System.out.println("\n清空列表...");
        students.clear();
        System.out.println("列表是否为空? " + students.isEmpty());
        System.out.println("最终列表: " + students);
    }
}

ArrayList 是 Java 开发中必须掌握的核心工具,它像一个功能更强大的“动态数组”,解决了原生数组长度固定的痛点,记住它的核心特点:

  • 基于数组,随机访问快。
  • 非线程安全
  • 有序、可重复
  • 通过泛型保证类型安全。

熟练掌握 ArrayList 的创建、增、删、改、查、遍历等基本操作,是进行 Java 开发的基础。

分享:
扫描分享到社交APP
上一篇
下一篇