为了账号安全,请及时绑定邮箱和手机立即绑定

面试高频陷阱:for vs foreach,你真的分清了吗?

标签:
Java JavaScript

面试高频陷阱:for vs foreach,你真的分清了吗?

引子:一次线上故障引发的思考

上个月,我们系统突然出现了性能问题,CPU使用率飙升到90%。排查后发现,罪魁祸首竟然是一段看似"无害"的foreach循环!

那段代码的作者振振有词:"foreach不是比传统for循环更优雅、更安全吗?"这句话让我想起了自己刚工作时的天真想法——以为foreach就是for的完美替代品。

探索:两种循环的"真面目"

传统for:老兵不死

传统for循环就像是一把瑞士军刀,虽然看起来朴素,但功能强大,控制精确:

List<String> list = Arrays.asList("Java", "Python", "Go");

// 传统for:完全控制
for (int i = 0; i < list.size(); i++) {
    String lang = list.get(i);
    // 可以获取索引,可以倒序,可以跳跃...
    if (i % 2 == 0) {
        System.out.println("偶数位置:" + lang);
    }
}

Enhanced for:优雅但有限制

Enhanced for(foreach)就像是自动档汽车,开起来舒服,但失去了一些控制权:

// foreach:简洁优雅
for (String lang : list) {
    System.out.println("语言:" + lang);
    // 但是...拿不到索引,不能反向遍历
}

转折:踩坑瞬间

第一个坑:性能陷阱

还记得开头提到的线上故障吗?问题就出在这里:

// 看似无害的foreach,实际是性能杀手
LinkedList<Integer> linkedList = new LinkedList<>();
// ... 添加10万个元素

// 这行代码让CPU哭泣 😭
for (int i = 0; i < linkedList.size(); i++) {
    Integer value = linkedList.get(i); // 每次get(i)都是O(n)操作!
    // ...
}

LinkedList的get(i)方法需要从头开始遍历,时间复杂度是O(n),整个循环就变成了O(n²)!

第二个坑:修改元素的陷阱

List<String> fruits = new ArrayList<>(Arrays.asList("苹果", "香蕉", "坏橘子"));

// 危险操作:在foreach中删除元素
for (String fruit : fruits) {
    if (fruit.contains("坏")) {
        fruits.remove(fruit); // 💥 ConcurrentModificationException
    }
}

解决:选择的智慧

性能对比真相

场景 传统for foreach 推荐
ArrayList遍历 foreach(可读性好)
LinkedList遍历 极慢 foreach
需要索引 传统for
修改集合 传统for
纯遍历 foreach

安全删除的正确姿势

// 方案1:传统for倒序删除
for (int i = fruits.size() - 1; i >= 0; i--) {
    if (fruits.get(i).contains("坏")) {
        fruits.remove(i);
    }
}

// 方案2:Iterator(foreach底层就是这个)
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
    if (iterator.next().contains("坏")) {
        iterator.remove(); // 安全删除
    }
}

经验启示

选择原则

用foreach的场景:

  • 纯粹的遍历操作
  • 不需要索引信息
  • 追求代码简洁性
  • 集合类型是ArrayList、HashSet等

用传统for的场景:

  • 需要索引操作
  • 要修改集合结构
  • 性能敏感且使用LinkedList
  • 需要复杂的循环控制

面试反杀技巧

当面试官问"for和foreach有什么区别"时,别只说语法差异,直接抛出这几个点:

  1. 底层实现:foreach是语法糖,编译后变成Iterator
  2. 性能差异:LinkedList场景下的巨大差异
  3. 安全性:ConcurrentModificationException的产生和避免
  4. 适用场景:具体场景具体分析

踩坑教训

  • 不要盲目追求"优雅":代码优雅不等于性能优秀
  • 了解集合特性:ArrayList和LinkedList的内部实现差异巨大
  • 测试驱动选择:性能敏感场景要做基准测试

总结

for和foreach的选择,表面上是语法问题,实际上考验的是对Java集合框架的深度理解。

记住这个口诀:纯遍历用foreach,要索引用for,LinkedList慎用传统for,修改集合选Iterator

最重要的是,不要被"新语法更好"的刻板印象束缚。技术选型的核心永远是:场景适配,性能优先

本文转自渣哥https://zha-ge.cn/java/12

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消