能自动调整大小的列表
普通的 Java 数组长度是固定的:定下十个位置,就永远只有十个位置。ArrayList 是 java.util 中可变长度的替代方案:添加元素时它会变长,删除时会变短,还自带各种实用方法,满足你对列表的日常操作。
注意尖括号里的类型 List<String>,它告诉编译器这个列表存放的是 String 值。右边的 <>(“菱形运算符”)让 Java 推断出同样的类型,不必再写一遍。另外注意要导入 java.util.ArrayList;它默认是不可用的。
声明为 List,构造为 ArrayList
你几乎总会看到变量被声明为 List 接口类型,而不是具体的 ArrayList:
List<String> names = new ArrayList<>();
这是好习惯,不是硬性规定。面向 List 接口编程,意味着其余代码并不关心你用的是哪种列表实现,以后换成另一种实现时无需改动它们。在日常使用中,两种写法的效果相同。
添加、获取和设置
add(value)追加到末尾。add(index, value)插入到指定位置,后面的元素依次后移。get(index)读取指定位置的元素(索引从 0 开始)。set(index, value)覆盖已有的元素。
索引从 0 开始,对越界的索引调用 get 会抛出 IndexOutOfBoundsException。
删除元素
当列表存放的是 Integer 时,有一个经典陷阱。remove(int) 是“按索引删除”,remove(Object) 是“按值删除”,所以:
List<Integer> nums = new ArrayList<>(List.of(10, 20, 30));
nums.remove(1); // removes index 1 -> the value 20
nums.remove(Integer.valueOf(20)); // removes the value 20
当你的意思是“删除这个值”而不是“删除这个索引”时,要用 Integer.valueOf(...) 包装这个值。
大小、包含与索引查找
List.of(...) 可以快速创建一个不可变列表;把它传给 ArrayList 构造方法,就得到一个以这些值为初始内容的可变副本。
遍历 ArrayList
最简洁的循环是增强 for 循环(for-each 循环):
如果还需要索引,就用 size() 和 get(i) 写计数循环:
有一条规则:不要在 for-each 循环遍历列表时向列表添加或删除元素,否则会抛出 ConcurrentModificationException。要安全地删除匹配的元素,使用 removeIf:
排序
Collections.sort 按自然顺序原地排序列表(字符串按字母顺序,数字按大小):
要自定义排序,给 list.sort(...) 传入一个 Comparator,例如 names.sort(Comparator.comparingInt(String::length)) 按长度排序。
ArrayList 存放对象,而不是基本类型
你不能写 ArrayList<int>。泛型只能用于对象类型,所以要使用包装类 Integer、Double、Boolean 等:
Java 的自动装箱会自动在 int 和 Integer 之间转换,所以代码读起来很自然,只是要记住列表本身存放的是 Integer 对象。
下一步:HashMap
当你关心顺序和位置时,ArrayList 是合适的工具。当你需要按键查找数据时,比如用用户名找用户、用商品编码找价格,你需要的是 HashMap,这就是下一页的内容。
常见问题
Java 中如何创建 ArrayList?
在尖括号中写出元素类型来声明它,并调用构造方法:ArrayList<String> names = new ArrayList<>();。右边的 <>(菱形运算符)让 Java 自动推断类型。通常把变量声明为 List 接口类型:List<String> names = new ArrayList<>();。
Java 中数组和 ArrayList 有什么区别?
普通数组的长度在创建时就固定了,可以存放 int 这样的基本类型。ArrayList 会随着添加和删除元素自动变长或变短,只能存放对象(所以 int 要变成 Integer),并且自带 add、remove、contains 和 size 等方法。固定大小的基本类型数据用数组;大小会变化时用 ArrayList。
如何从 ArrayList 中删除元素?
调用 remove(index) 按位置删除,或调用 remove(object) 删除第一个匹配的元素。对 Integer 列表要小心:list.remove(2) 删除的是索引 2,而 list.remove(Integer.valueOf(2)) 删除的是值 2。要在遍历时删除,使用 Iterator 的 remove() 或 removeIf(...),以避免 ConcurrentModificationException。