答案是通过防御性编程、正确选择集合类型、使用泛型和迭代器等手段可有效避免Java集合异常。具体包括:操作前检查null和索引,使用Optional处理可能为空的对象;遍历时用Iterator.remove()或removeIf()避免ConcurrentModificationException;多线程场景选用ConcurrentHashMap或CopyOnWriteArrayList;禁止修改不可变集合如List.of()返回的实例;始终使用泛型防止ClassCastException,杜绝原始类型以确保类型安全。

Java集合框架中的异常处理,说到底,更多是一种防御性编程的艺术,而不是单纯地用
try-catch
处理Java集合框架中的异常,我们通常会遇到几类“老朋友”:
NullPointerException
IndexOutOfBoundsException
UnsupportedOperationException
ConcurrentModificationException
ClassCastException
null
null
if (list != null && !list.isEmpty())
NullPointerException
List
if (index >= 0 && index < list.size())
java.util.concurrent
ConcurrentHashMap
CopyOnWriteArrayList
ArrayList
synchronized
ConcurrentModificationException
Iterator
remove()
for
remove()
add()
ConcurrentModificationException
ClassCastException
Collections.unmodifiableList()
List.of()
Set.of()
UnsupportedOperationException
NullPointerException
首先,最直接的办法就是显式检查。每次从Map里取值,或者对一个可能为空的集合进行操作前,都习惯性地加个
null
立即学习“Java免费学习笔记(深入)”;
Map<String, String> myMap = getSomeMap();
if (myMap != null) {
String value = myMap.get("key");
if (value != null) {
// 对value进行操作
}
}这虽然看起来有点啰嗦,但在关键路径上,它是最稳妥的。当然,Java 8 引入的
Optional
null
Optional.ofNullable(getSomeMap())
.map(map -> map.get("key"))
.ifPresent(value -> {
// 对value进行操作
});用
Optional
null
if
其次,防御性地处理输入。如果你的方法接收一个集合作为参数,而你又不确定调用方会不会传
null
public void processCollection(List<String> data) {
if (data == null) {
// 可以抛出IllegalArgumentException,或者创建一个空列表,或者直接返回
System.out.println("Input data is null, skipping processing.");
return;
}
// ... 对data进行操作
}甚至,如果你要往集合里添加元素,但又不允许添加
null
Objects.requireNonNull()
List<String> names = new ArrayList<>(); names.add(Objects.requireNonNull(name, "Name cannot be null"));
这会在
name
null
NullPointerException
ConcurrentModificationException
for
add()
remove()
要处理CME,关键在于理解它的触发机制,然后选择正确的工具。
理解“fail-fast”: 大多数
java.util
ArrayList
HashMap
modCount
set
安全地遍历并修改:
使用迭代器自身的remove()
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if ("b".equals(element)) {
iterator.remove(); // 安全删除
}
}
System.out.println(list); // 输出: [a, c, d]遍历副本: 如果你需要进行添加或更复杂的修改,最简单粗暴但有效的方法是遍历集合的一个副本。
List<String> originalList = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (String element : new ArrayList<>(originalList)) { // 遍历副本
if ("b".equals(element)) {
originalList.add("x"); // 修改原列表,不会触发CME
}
}
System.out.println(originalList); // 输出: [a, b, c, x]Java 8 removeIf()
removeIf()
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
list.removeIf(element -> "b".equals(element));
System.out.println(list); // 输出: [a, c, d]使用并发集合: 在多线程环境下,如果你确实需要并发地读写集合,那么
java.util.concurrent
CopyOnWriteArrayList
CopyOnWriteArraySet
ConcurrentHashMap
Collections.synchronizedList()
synchronizedMap()
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// ... 添加元素
synchronized (syncList) { // 必须同步迭代块
for (String element : syncList) {
// ...
}
}在我看来,CME的出现,很多时候是设计者没有充分考虑到集合的生命周期和并发访问模式。选择正确的集合类型,或者在迭代时使用正确的方法,是避免这个问题的根本。
这两种异常,一个代表着“我不想干这活儿”,另一个则是“你给我的东西不对付”。它们不像NPE那么频繁,但一旦出现,往往意味着你对集合的特性或者类型系统理解上有些偏差。
这个异常通常发生在当你试图对一个不可修改的集合执行修改操作时。这并非一个错误,而是集合设计者明确告诉你:“这个集合就是用来读的,你别想改它。”
常见场景:
Collections.unmodifiableList()
unmodifiableSet()
unmodifiableMap()
add()
remove()
set()
List<String> original = new ArrayList<>(Arrays.asList("a", "b"));
List<String> unmodifiableList = Collections.unmodifiableList(original);
// unmodifiableList.add("c"); // 抛出 UnsupportedOperationExceptionArrays.asList()
List
add()
remove()
List<String> fixedSizeList = Arrays.asList("x", "y");
// fixedSizeList.add("z"); // 抛出 UnsupportedOperationException
fixedSizeList.set(0, "a"); // 可以修改元素,但不能改变大小List.of()
Set.of()
Map.of()
List<String> immutableList = List.of("apple", "banana");
// immutableList.add("orange"); // 抛出 UnsupportedOperationException预防措施:
List<String> externalList = getSomeList(); // 可能是一个不可修改的List
List<String> mutableCopy = new ArrayList<>(externalList);
mutableCopy.add("new element"); // 现在可以安全修改了这个异常意味着你试图将一个对象强制转换为它实际上不是的类型。在集合框架中,它主要与泛型的缺失或误用有关。
常见场景:
使用原始类型(Raw Types): 在Java 5之前,集合没有泛型。如果你在现代Java代码中仍然使用
List list = new ArrayList();
List rawList = new ArrayList();
rawList.add("Hello");
rawList.add(123); // 编译器不报错
// ... 稍后
String s = (String) rawList.get(1); // 运行时抛出 ClassCastException: Integer cannot be cast to String类型擦除的陷阱(较少见,更高级): 泛型在运行时会被擦除,这在某些反射或特殊场景下可能导致意外。但对于日常的集合使用,只要正确声明泛型,通常不会遇到这个问题。
预防措施:
List<String> typedList = new ArrayList<>();
typedList.add("Hello");
// typedList.add(123); // 编译时报错,完美!
String s = typedList.get(0); // 无需强制转换,类型安全List<Object>
instanceof
在我看来,
UnsupportedOperationException
ClassCastException
以上就是Java中集合框架常用异常处理方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号