Java:ArrayList的使用

目录

一、概念

二、常用操作

2.1 初始化

2.2 CRUD

2.3 遍历

2.4 排序

2.5 其他常用方法


一、概念

ArrayList 是 Java java.util 包中的一个类,它实现了 List 接口,底层基于一个可动态扩容的数组。它提供了对元素的快速随机访问,但在列表中间进行插入和删除操作通常效率较低。

核心特点:

  • 有序集合(Ordered):元素按照插入的顺序存放,你可以通过索引(从0开始)来访问和操作元素。

  • 允许重复元素(Allows Duplicates):可以添加多个相同的元素(包括 null)。

  • 动态扩容(Resizable):与标准数组不同,ArrayList 的容量会自动增长,以容纳新添加的元素。你无需在创建时就指定一个最终的大小。

无参构造:默认会初始化一个空数组 (Object[])。只有在第一次添加元素(add())时,才会将容量真正扩容到默认容量 10

带初始容量构造:指定初始容量。

动态扩容 :新容量通常是旧容量的 1.5 倍 (即 oldCapacity + (oldCapacity >> 1))计算新容量->创建新数组->拷贝数据->替换引用

没有自动缩容:删除元素后,空出之后的部分往前移动,不会主动回收多余内存空间。但 ArrayList 提供了一个公共方法 trimToSize() 来让你手动进行缩容。

  • 非线程安全(Not Synchronized):默认情况下,ArrayList 不是线程安全的。如果多个线程同时访问一个 ArrayList 实例,并且至少有一个线程在结构上修改了列表(如添加、删除),则必须在外部进行同步。(可以使用 Collections.synchronizedList 来包装它,或在多线程环境下考虑使用 CopyOnWriteArrayList)。

二、常用操作

2.1 初始化

// 1. 创建一个初始容量为10的空列表(最常用)
ArrayList<String> list1 = new ArrayList<>(); // Java 7+ 钻石语法

// 2. 创建一个指定初始容量的空列表(如果你能预估元素数量,指定容量可避免初期频繁扩容,提升性能)
ArrayList<Integer> list2 = new ArrayList<>(50);

// 3. 创建一个包含指定集合所有元素的列表
ArrayList<String> existingList = new ArrayList<>();
existingList.add("A");
existingList.add("B");
ArrayList<String> list3 = new ArrayList<>(existingList); // list3 包含 ["A", "B"]

关键点: 一定要使用泛型(如 <String><Integer>)来指定列表将要持有的对象类型。这确保了类型安全,避免了强制类型转换的麻烦。

2.2 CRUD

ArrayList<String> fruits = new ArrayList<>();

// 添加元素
// add(E element):将元素追加到列表的末尾。
fruits.add("Apple");  // ["Apple"]
fruits.add("Banana"); // ["Apple", "Banana"]
fruits.add(null);     // ["Apple", "Banana", null] (允许null)
// add(int index, E element):将元素插入到指定的索引位置。
fruits.add(1, "Orange"); // ["Apple", "Orange", "Banana", null]

// 获取元素
// get(int index):返回指定索引位置的元素。
String firstFruit = fruits.get(0); // "Apple"
String thirdFruit = fruits.get(2); // "Banana"
// String invalid = fruits.get(10); // 抛出 IndexOutOfBoundsException

// 修改元素
// set(int index, E element):用指定元素替换列表中指定位置的元素,返回被替换的旧元素。
String oldFruit = fruits.set(2, "Mango"); // oldFruit = "Banana" 现在列表是:["Apple", "Orange", "Mango", null]

// 删除元素
// remove(int index):移除指定索引位置的元素,返回被移除的元素。
String removedFruit = fruits.remove(0); // removedFruit = "Apple" 现在列表是:["Orange", "Mango", null]
// remove(Object o):移除列表中第一次出现的指定元素(如果存在)。成功移除返回 true,否则返回 false。
boolean isRemoved = fruits.remove("Mango"); // isRemoved = true, 列表变为 ["Orange", null]
isRemoved = fruits.remove("Grape");         // isRemoved = false, 列表无变化

// 获取列表大小
// size():返回列表中元素的数量(不是底层数组的容量!)。
int count = fruits.size(); // 现在是 2

// 检查是否包含元素
// contains(Object o):如果列表包含指定的元素,则返回 true。
boolean hasApple = fruits.contains("Apple"); // false
boolean hasNull = fruits.contains(null);     // true

// 清空列表
// clear():移除列表中的所有元素。
fruits.clear(); // 列表变为空 []
System.out.println(fruits.size()); // 0

2.3 遍历

// 1.使用 for 循环(通过索引)
for (int i = 0; i < fruits.size(); i++) {
    System.out.println("Fruit at index " + i + ": " + fruits.get(i));
}


// 2.使用 for-each 循环(增强for循环) - 最简洁、最常用
for (String fruit : fruits) {
    System.out.println(fruit);
}

// 3.使用迭代器 (Iterator)
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
    String fruit = iterator.next();
    System.out.println(fruit);
    // 可以在遍历中使用 iterator.remove() 安全地删除当前元素
    // 但不能在迭代过程中使用 ArrayList 的 add/remove 方法,否则会抛出 ConcurrentModificationException
}

// 4.使用 forEach() 方法 (Java 8+)
fruits.forEach(fruit -> System.out.println(fruit));
// 或使用方法引用
fruits.forEach(System.out::println);

2.4 排序

// 对可比较对象排序(如 String, Integer)
ArrayList<String> list = new ArrayList<>();
list.add("Zebra");
list.add("Apple");
list.add("Mango");

Collections.sort(list); // 自然顺序排序:["Apple", "Mango", "Zebra"]


// 使用 Comparator 自定义排序规则
// 按字符串长度排序
Collections.sort(list, (s1, s2) -> s1.length() - s2.length());
// 或者
Collections.sort(list, Comparator.comparingInt(String::length));

2.5 其他常用方法

ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
fruits.add("Apple"); // 添加重复元素
fruits.add("Mango");

// isEmpty():检查列表是否为空(size() == 0)。
System.out.println("列表是否为空: " + fruits.isEmpty()); // false

// indexOf(Object o):返回指定元素在列表中第一次出现的索引,如果不存在则返回 -1。
int firstAppleIndex = fruits.indexOf("Apple");
System.out.println("\"Apple\" 第一次出现的索引: " + firstAppleIndex); // 0
int grapeIndex = fruits.indexOf("Grape");
System.out.println("\"Grape\" 的索引 (不存在): " + grapeIndex); // 返回 -1

// lastIndexOf(Object o):返回指定元素在列表中最后一次出现的索引。
int lastAppleIndex = fruits.lastIndexOf("Apple");
System.out.println("\"Apple\" 最后一次出现的索引: " + lastAppleIndex); // 3

// toArray():将 ArrayList 转换为一个Object数组。
Object[] fruitsArray = fruits.toArray();
System.out.println("转换为 Object 数组: " + Arrays.toString(fruitsArray)); // [Apple, Banana, Orange, Apple, Mango]

// toArray(T[] a):将 ArrayList 转换为指定类型的数组(更常用)。
String[] fruitsStringArray = fruits.toArray(new String[0]);
System.out.println("转换为 String 数组: " + Arrays.toString(fruitsStringArray)); // [Apple, Banana, Orange, Apple, Mango]
// 预分配大小的数组
String[] sizedArray = new String[fruits.size()];
fruits.toArray(sizedArray);
System.out.println("使用预分配数组: " + Arrays.toString(sizedArray)); // [Apple, Banana, Orange, Apple, Mango]

// addAll(Collection c) / addAll(int index, Collection c):将另一个集合的所有元素添加到当前列表的末尾或指定位置。
ArrayList<String> moreFruits = new ArrayList<>();
moreFruits.add("Grape");
moreFruits.add("Pineapple");
fruits.addAll(moreFruits);
System.out.println("添加更多水果后: " + fruits); // [Apple, Banana, Orange, Apple, Mango, Grape, Pineapple]
// 在指定位置添加
ArrayList<String> citrusFruits = new ArrayList<>();
citrusFruits.add("Lemon");
citrusFruits.add("Lime");
fruits.addAll(2, citrusFruits);
System.out.println("在索引2处添加柑橘类水果后: " + fruits); // [Apple, Banana, Lemon, Lime, Orange, Apple, Mango, Grape, Pineapple]

// subList(int fromIndex, int toIndex):返回列表中指定的 fromIndex(包含)到 toIndex(不包含)之间的视图(View)。对子列表的修改会影响原始列表。
List<String> subList = fruits.subList(2, 5); // 从索引2(包含)到5(不包含)
System.out.println("子列表(2,5): " + subList); // [Lemon, Lime, Orange]
// 修改子列表会影响原始列表
subList.set(0, "ModifiedLemon");
System.out.println("修改子列表后原始列表: " + fruits); // [Apple, Banana, ModifiedLemon, Lime, Orange, Apple, Mango, Grape, Pineapple]
// 从原始列表删除子列表范围内的元素
fruits.subList(2, 5).clear();
System.out.println("清除子列表范围后: " + fruits); // [Apple, Banana, Apple, Mango, Grape, Pineapple]

注意:

  • ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异常,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。(subList 返回的是 ArrayList 的内部类 SubList)
  • 在 subList 场景中, 高度注意 对原集合元素的增加或删除,均会导致子列表的遍历、增加、删除产生 ConcurrentModificationException 异常。
    • 使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一致、长度为 0 的空数组。直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出现 ClassCastException 错误。
    • 在使用 Collection 接口任何实现类的 addAll()方法时,都要对输入的集合参数进行 NPE 判断。在 ArrayList#addAll 方法的第一行代码即 Object[] a = c.toArray(); 其中 c 为输入集合参数,如果为 null,则直接抛出异常。
    • 使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。 asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList 体现的是适 配器模式,只是转换接口,后台的数据仍是数组。
    • 不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。
    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
    红包 添加红包
    表情包 插入表情
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包

    打赏作者

    熙客

    你的鼓励将是我创作的最大动力

    ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
    扫码支付:¥1
    获取中
    扫码支付

    您的余额不足,请更换扫码支付或充值

    打赏作者

    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

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

    余额充值