ArrayList 是 Java 中最常用的集合类之一,它基于动态数组实现,查询操作是使用 ArrayList 时非常频繁的需求,我们将从以下几个方面来深入探讨:

- 最常用的查询方法
- 查询方法的性能分析
- 高级查询技巧
- 最佳实践和注意事项
最常用的查询方法
ArrayList 提供了多种查询方法,以满足不同的使用场景。
a. 根据索引查询元素 - get(int index)
这是最基本、最直接的查询方式,通过元素在列表中的位置(索引)来获取元素。
- 方法签名:
E get(int index) - 功能: 返回列表中指定位置的元素。
- 参数:
index- 要返回元素的索引(从 0 开始)。 - 返回值: 指定位置上的元素。
- 异常: 如果索引
index超出范围(index < 0 || index >= size()),会抛出IndexOutOfBoundsException。
示例代码:
import java.util.ArrayList;
public class ArrayListGetExample {
public static void main(String[] args) {
ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
fruits.add("Mango");
// 获取索引为 2 的元素
String fruit = fruits.get(2);
System.out.println("元素 at index 2: " + fruit); // 输出: 元素 at index 2: Orange
// 获取第一个元素 (index = 0)
String firstFruit = fruits.get(0);
System.out.println("第一个元素: " + firstFruit); // 输出: 第一个元素: Apple
// 获取最后一个元素
String lastFruit = fruits.get(fruits.size() - 1);
System.out.println("最后一个元素: " + lastFruit); // 输出: 最后一个元素: Mango
// 错误示例:索引越界
try {
String invalidFruit = fruits.get(5);
} catch (IndexOutOfBoundsException e) {
System.out.println("捕获到异常: " + e.getMessage()); // 输出: 捕获到异常: Index: 5, Size: 4
}
}
}
b. 检查元素是否存在 - contains(Object o)
当你想知道列表中是否包含某个特定值时,可以使用此方法。

- 方法签名:
boolean contains(Object o) - 功能: 如果列表中包含指定的元素,则返回
true,否则返回false。 - 内部实现: 它会遍历整个列表,使用
equals()方法逐个比较元素。重写你自定义类的equals()和hashCode()方法对于此方法至关重要。
示例代码:
import java.util.ArrayList;
public class ArrayListContainsExample {
public static void main(String[] args) {
ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
boolean hasApple = fruits.contains("Apple");
System.out.println("列表中是否包含 'Apple'? " + hasApple); // 输出: true
boolean hasGrape = fruits.contains("Grape");
System.out.println("列表中是否包含 'Grape'? " + hasGrape); // 输出: false
}
}
c. 查找元素的索引 - indexOf(Object o) 和 lastIndexOf(Object o)
如果你想知道某个元素在列表中的位置,可以使用这两个方法。
-
indexOf(Object o):- 功能: 返回此列表中第一次出现的指定元素的索引;如果列表不包含该元素,则返回
-1。 - 内部实现: 同样,它会从列表头部开始遍历,使用
equals()进行比较。
- 功能: 返回此列表中第一次出现的指定元素的索引;如果列表不包含该元素,则返回
-
lastIndexOf(Object o):
(图片来源网络,侵删)- 功能: 返回此列表中最后一次出现的指定元素的索引;如果列表不包含该元素,则返回
-1。 - 内部实现: 从列表尾部开始向前遍历。
- 功能: 返回此列表中最后一次出现的指定元素的索引;如果列表不包含该元素,则返回
示例代码:
import java.util.ArrayList;
public class ArrayListIndexExample {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(20);
numbers.add(40);
int firstIndex = numbers.indexOf(20);
System.out.println("20 第一次出现的索引: " + firstIndex); // 输出: 1
int lastIndex = numbers.lastIndexOf(20);
System.out.println("20 最后一次出现的索引: " + lastIndex); // 输出: 3
int notFoundIndex = numbers.indexOf(99);
System.out.println("99 的索引: " + notFoundIndex); // 输出: -1
}
}
查询方法的性能分析
理解 ArrayList 的底层实现是分析其性能的关键。ArrayList 内部是一个动态数组。
-
get(int index):- 时间复杂度: O(1) - 常数时间
- 原因:
ArrayList的底层数组在内存中是连续存储的,通过索引访问元素,可以直接计算出内存地址,就像访问普通数组一样,速度非常快,这是ArrayList相比LinkedList最大的优势之一。
-
contains(Object o),indexOf(Object o),lastIndexOf(Object o):- 时间复杂度: O(n) - 线性时间
- 原因: 这些方法在找不到元素时,最坏的情况需要遍历整个列表中的所有
n个元素,逐个调用equals()进行比较,当列表很大时,性能会明显下降。
高级查询技巧
当需要对 ArrayList 进行更复杂的查询时,比如按条件筛选、查找特定对象等,可以借助 Java 8 引入的 Stream API 或传统的循环。
a. 使用 Java 8 Stream API (推荐)
Stream API 提供了声明式的方式来处理集合,代码更简洁、易读。
场景1: 查找第一个满足条件的元素
使用 stream().filter().findFirst()
import java.util.ArrayList;
import java.util.Optional;
class Person {
private String name;
private int age;
// 构造函数, getter, setter, toString() ...
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
public class ArrayListStreamExample {
public static void main(String[] args) {
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Alice", 30));
people.add(new Person("Bob", 25));
people.add(new Person("Charlie", 35));
// 查找第一个年龄大于 30 的人
Optional<Person> personOptional = people.stream()
.filter(p -> p.getAge() > 30)
.findFirst();
if (personOptional.isPresent()) {
System.out.println("找到的人: " + personOptional.get().getName()); // 输出: 找到的人: Charlie
} else {
System.out.println("没有找到符合条件的用户。");
}
}
}
注意: Optional 是一个容器类,它可以包含或不包含非 null 的值,使用 isPresent() 检查是否存在值,或者使用 ifPresent() 进行消费,可以避免 NullPointerException。
场景2: 查找所有满足条件的元素
使用 stream().filter().collect(Collectors.toList())
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class ArrayListStreamFilterExample {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
// 筛选出所有偶数
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("所有偶数: " + evenNumbers); // 输出: 所有偶数: [2, 4, 6, 8, 10]
}
}
b. 使用传统的 for-each 循环
在不使用 Java 8 或希望代码更兼容旧环境时,传统的循环是可靠的选择。
import java.util.ArrayList;
import java.util.List;
public class ArrayListForLoopExample {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
List<Integer> evenNumbers = new ArrayList<>();
for (Integer number : numbers) {
if (number % 2 == 0) {
evenNumbers.add(number);
}
}
System.out.println("所有偶数: " + evenNumbers); // 输出: 所有偶数: [2, 4, 6, 8, 10]
}
}
最佳实践和注意事项
-
为自定义类实现
equals()和hashCode()- 这是使用
contains(),indexOf(),remove()等基于对象内容比较的方法的前提。 - 如果你没有重写
equals(),这些方法会使用Object类的默认实现(即比较对象的内存地址),这通常不是你想要的结果。
// 好的做法 class Person { private String name; // ... 其他字段 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name); } } - 这是使用
-
避免在
for-each循环中修改ArrayList- 在
for-each循环内部调用list.add()或list.remove()会抛出ConcurrentModificationException。 - 解决方案:
- 如果需要删除元素,使用迭代器 (
Iterator) 的remove()方法。 - 或者使用 Java 8 的
removeIf()方法,它既安全又高效。
- 如果需要删除元素,使用迭代器 (
// 错误示例 // for (String item : list) { // if (item.equals("bad")) { // list.remove(item); // 抛出 ConcurrentModificationException // } // } // 正确示例 1: 使用 Iterator // Iterator<String> iterator = list.iterator(); // while (iterator.hasNext()) { // String item = iterator.next(); // if (item.equals("bad")) { // iterator.remove(); // } // } // 正确示例 2: 使用 removeIf (推荐) list.removeIf(item -> item.equals("bad")); - 在
-
为频繁的查询操作选择正确的数据结构
- 如果你的应用场景是查询多、增删少,并且需要通过索引快速访问,
ArrayList是最佳选择。 - 如果你的应用场景是增删多、查询多,并且查询条件复杂(需要根据多个字段频繁查找),可以考虑使用
HashMap或HashSet,它们的查询复杂度是 O(1),性能远超ArrayList的 O(n)。
- 如果你的应用场景是查询多、增删少,并且需要通过索引快速访问,
| 方法 | 功能 | 时间复杂度 | 备注 |
|---|---|---|---|
get(int index) |
通过索引获取元素 | O(1) | ArrayList 的核心优势,速度最快。 |
contains(Object o) |
检查元素是否存在 | O(n) | 需遍历列表,依赖 equals() 方法。 |
indexOf(Object o) |
查找元素首次出现的索引 | O(n) | 需遍历列表,依赖 equals() 方法。 |
lastIndexOf(Object o) |
查找元素末次出现的索引 | O(n) | 需遍历列表,依赖 equals() 方法。 |
| Stream API | 复杂查询(筛选、查找等) | O(n) | 代码简洁,功能强大,是现代 Java 的首选。 |
| 传统 for 循环 | 通用遍历和查询 | O(n) | 兼容性好,逻辑清晰,但代码量稍多。 |
希望这份详细的指南能帮助你更好地理解和使用 Java ArrayList 的查询功能!
