Java 集合实战:从“能用”到“高手”的避坑与优化指南
前言
很多同学面试时对 HashMap 的红黑树对答如流,但在写代码时却经常写出 Arrays.asList() 导致的崩溃,或者在循环中删除元素导致的异常。本篇将从实战角度出发,梳理 Java 集合在使用过程中的六大核心注意事项。
一、 集合判空:不要只依赖 null 检查
在业务逻辑中,判断集合是否有元素,推荐使用 isEmpty() 而不是 size() == 0,更不能只靠 != null。
-
规范建议:
-
使用
Collection.isEmpty(),语义更清晰,性能在某些特定集合(如ConcurrentLinkedQueue)下优于size()。 -
推荐使用工具类:
CollectionUtils.isEmpty(collection)(Apache Commons 或 Spring 提供),它能同时帮你做非空和长度为 0 的判断。
-
Java
// ✅ 推荐做法
if (CollectionUtils.isNotEmpty(list)) {
// 执行业务逻辑
}
二、 集合转数组:必须使用 toArray(T[] array)
当我们需要把一个 List 转换成数组时,千万不要直接使用无参的 toArray()。
-
避坑点:无参
toArray()返回的是Object[]数组,强转成其他类型会抛出ClassCastException。 -
最佳实践: 使用
toArray(T[] array),并传入一个长度为 0 的数组。
Java
List<String> list = new ArrayList<>(Arrays.asList("Java", "Go"));
// ✅ 推荐做法:使用长度为 0 的数组
String[] array = list.toArray(new String[0]);
原理分析:传入
new String[0]既能保证类型安全,又避免了预先计算长度的开销,因为 JVM 内部会对这种写法进行优化,比预设长度性能更好。
三、 数组转集合:谨防 Arrays.asList() 的陷阱
这是新手最容易掉进去的坑。
1. 它是“伪 List”
Arrays.asList() 返回的是 Arrays 内部的一个静态内部类,它并没有实现 add() 和 remove() 方法。
-
结果:如果你对它执行
add或remove,会直接抛出UnsupportedOperationException。
2. 它是“联动的”
这个 List 底层直接引用了原数组。如果你修改了原数组的值,List 里的值也会跟着变。
3. 基础类型不支持
如果你传入一个 int[],它不会转成 List<Integer>,而是转成了一个 List<int[]>,这通常不是你想要的结果。
✅ 正确姿势:
Java
// 如果需要对转换后的 List 进行增删
List<String> list = new ArrayList<>(Arrays.asList("A", "B"));
// Java 9+ 推荐
List<String> list2 = List.of("A", "B");
四、 集合遍历:禁止在 for-each 中增删元素
这是一个经典的面试题,也是高频的线上 Bug。
-
错误现象:在
for循环或for-each中调用list.remove(),会抛出ConcurrentModificationException(CME)。 -
原理解析:Java 的集合在迭代时维护了一个
modCount(修改计数器),如果你在迭代过程中通过集合自带的方法修改了长度,计数器就会对不上,触发fail-fast机制。
✅ 正确姿势:
-
使用
Iterator的remove()方法。 -
Java 8+ 使用
removeIf()方法。
Java
// ✅ 推荐做法 (Java 8+)
list.removeIf(filter -> filter.contains("target"));
五、 集合去重:不仅仅是 distinct()
很多人喜欢用 list.stream().distinct() 来去重,虽然方便,但如果数据量巨大,性能远不如 HashSet。
-
性能对比:
-
HashSet利用哈希表,查找重复元素的效率接近 O(1)。 -
如果对一个大列表去重,先转成
HashSet再转回List往往是最高效的。
-
Java
// 如果不要求顺序 Set<String> set = new HashSet<>(largeList); List<String> result = new ArrayList<>(set);
六、 集合排序:不可忽视的稳定性
使用 Collections.sort() 或 list.sort() 时,底层使用的是 TimSort(一种混合排序算法)。
-
注意事项:
-
稳定性:Java 的集合排序是稳定的(相同元素的相对位置不会变)。
-
比较器规范:实现
Comparator时,必须满足 自反性、传递性、对称性。如果逻辑不严谨(例如在大规模数据下a > b返回 1,但忘记处理a == b返回 0 的情况),可能会在运行时抛出Comparison method violates its general contract!异常。
-
七、 总结:资深开发的代码守则
-
明确场景:读多写少用
ArrayList,去重用HashSet,并发用ConcurrentHashMap。 -
防御式编程:返回集合方法时,如果没数据,返回
Collections.emptyList()而不是null,可以有效减少调用方的空指针风险。 -
性能意识:大集合初始化记得指定
initialCapacity。 -
安全意识:不可变场景下,使用
Collections.unmodifiableList()包装,防止集合被下游业务意外修改。
结语: Java 集合框架就像一套精密的工具箱,用好了事半功倍,用错了处处是坑。希望这三篇集合系列文章能帮你夯实基础,在面试和实战中游刃有余!
喜欢这篇文章吗?欢迎在评论区留言讨论!
更多推荐



所有评论(0)