Java集合遍历案例怎么编写:从基础到高级的完整指南
目录导读
-
Java集合遍历的五大经典方式

-
各遍历方式的代码案例与性能对比
-
遍历过程中如何安全删除元素
-
常见遍历错误与最佳实践
-
问答环节:面试高频问题解析
Java集合遍历的五大经典方式
在Java开发中,集合遍历是日常编码中最高频的操作之一,根据不同的JDK版本和场景需求,常见的遍历方式包括:
- 传统for循环:适用于List(如ArrayList、LinkedList)
- 增强for循环(for-each):兼容所有Collection接口实现
- 迭代器(Iterator):支持遍历时的安全删除操作
- Java 8 Stream API:支持函数式编程与链式操作
- ListIterator:支持双向遍历(仅适用于List)
理解这些方式的适用场景,能帮助你在编写代码时兼顾性能与可读性。
各遍历方式的代码案例与性能对比
ArrayList遍历(推荐使用for-each或Stream)
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Go");
// 方式1:增强for
for (String s : list) {
System.out.println(s);
}
// 方式2:Stream API(Java 8+)
list.stream().forEach(System.out::println);
// 方式3:传统for(性能最优,但代码略长)
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
性能对比:在ArrayList上,传统for循环通过索引直接访问,速度最快;增强for循环和Stream API性能相近,但在大数据集上Stream会有额外开销。
HashSet遍历(只能用迭代器或增强for)
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
// 方式1:迭代器
Iterator<Integer> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 方式2:for-each(本质也是迭代器)
for (Integer num : set) {
System.out.println(num);
}
HashMap遍历(四种方法)
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
// 方法1:遍历EntrySet(推荐,效率最高)
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
// 方法2:遍历KeySet然后取值(低效,需二次查询)
for (String key : map.keySet()) {
System.out.println(key + ":" + map.get(key));
}
// 方法3:Java 8 forEach
map.forEach((k, v) -> System.out.println(k + ":" + v));
// 方法4:迭代器遍历EntrySet
Iterator<Map.Entry<String, Integer>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Integer> entry = iter.next();
System.out.println(entry.getKey() + ":" + entry.getValue());
}
关键点:遍历HashMap时优先使用entrySet(),因为keySet()每次get()都会额外计算哈希值,在百万级数据时性能差异显著。
遍历过程中如何安全删除元素
这是Java集合面试中最高频的陷阱之一。直接在for-each循环中调用list.remove()会抛出ConcurrentModificationException。
错误示范:
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (String s : list) {
if (s.equals("b")) {
list.remove(s); // 抛出异常!
}
}
正确做法:
// 方法1:使用迭代器移除
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.equals("b")) {
it.remove(); // 安全,因为迭代器维护了modCount一致性
}
}
// 方法2:使用removeIf(Java 8+,推荐)
list.removeIf(s -> s.equals("b"));
// 方法3:收集需要删除的元素,遍历结束后统一删除
List<String> toRemove = new ArrayList<>();
for (String s : list) {
if (s.equals("b")) toRemove.add(s);
}
list.removeAll(toRemove);
特别注意:如果使用CopyOnWriteArrayList,可以在for-each中直接删除,但会增加内存复制开销。
常见遍历错误与最佳实践
错误1:在遍历中修改集合结构(增加元素)
// 会导致ConcurrentModificationException
for (String s : list) {
if (s.equals("a")) list.add("new");
}
解决:使用ListIterator的add()方法。
ListIterator<String> listIt = list.listIterator();
while (listIt.hasNext()) {
String s = listIt.next();
if (s.equals("a")) {
listIt.add("new"); // 在当前位置后插入
}
}
错误2:忽略空指针检查
// 如果collection为null,for-each会直接抛NPE
Collection<String> col = getCollection(); // 可能为null
if (col != null) {
for (String s : col) {...}
}
- 单线程:for-each 或 Stream(代码简洁)
- 需要删除:Iterator.remove() 或 removeIf()
- 需要下标:传统for循环(如更新指定位置元素)
- 高并发:使用并发集合如ConcurrentHashMap,其迭代器是弱一致性的
问答环节:面试高频问题解析
Q1: 为什么for-each遍历时不能添加或删除元素? A: for-each底层依赖Iterator,但内部没有暴露remove方法,当集合的modCount(修改次数)与迭代器记录的expectedModCount不一致时,会触发fast-fail机制,抛出ConcurrentModificationException,而Iterator.remove()会同步更新expectedModCount。
Q2: ArrayList和LinkedList的遍历性能差异有多大? A: ArrayList通过索引随机访问时间复杂度为O(1),传统for循环最快,LinkedList基于双向链表,get(i)需要遍历链表到索引位置(O(n)),因此LinkedList绝对不要用传统for循环! 应使用迭代器或for-each,它们使用指针遍历(O(n))。
Q3: Stream遍历会改变原始集合吗? A: 不会,Stream的forEach操作是只读的,如果需要修改集合元素,建议在forEach外部收集结果并使用collect()。
Q4: 遍历HashMap时,EntrySet和KeySet哪个更快? A: EntrySet更快,KeySet遍历时每次get(key)都需要调用hashCode()和equals(),而EntrySet直接获取键值对对象,在数据量超过100万条时,性能差距可达数倍。
Q5: 如何安全地在多线程环境下遍历集合? A: 1)使用java.util.concurrent包下的集合,如CopyOnWriteArrayList、ConcurrentHashMap;2)使用同步包装器Collections.synchronizedList(new ArrayList<>()),但需配合synchronized块进行迭代;3)使用显式锁(Lock)保证遍历与修改互斥。
通过以上案例和原理分析,你对“Java集合遍历案例怎么编写”应该有了系统性的认识,实际操作时,请根据集合类型、修改需求和性能要求,选择最合适的遍历方式,并严格遵守安全删除的规范。用对遍历方式,代码既能跑得快,又能跑得稳。