当前位置: 首页 > news >正文

Java集合学习之forEach()遍历方法的底层原理

        forEach()时Java集合中十分便捷的一个函数,但是很多人在使用很久后只知道其如何使用,但是并不知道其底层的实现原理时什么,Java是一门开源的语言,因此我们可以通过查阅底层的实现源代码来一窥其真容,这对我们未来集合的学习将起到至关重要的作用。

        集合的 forEach() 方法是遍历元素的便捷方式,其底层原理基于 函数式接口 和 迭代器模式

一.forEach的使用

我们先给出一段forEach() 的使用代码:

import java.util.*;public class ForEachExample {public static void main(String[] args) {// 1. List 遍历List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry");System.out.println("=== List 遍历 ===");fruits.forEach(fruit -> System.out.println("水果: " + fruit));// 2. Set 遍历Set<Integer> numbers = new HashSet<>(Arrays.asList(10, 20, 30, 40));System.out.println("\n=== Set 遍历 ===");numbers.forEach(number -> {int squared = number * number;System.out.println(number + "的平方: " + squared);});// 3. Map 遍历 (使用 BiConsumer)Map<String, Integer> priceMap = new HashMap<>();priceMap.put("iPhone", 6999);priceMap.put("iPad", 3299);priceMap.put("MacBook", 12999);}
}

输出结果如下:

=== List 遍历 ===
水果: Apple
水果: Banana
水果: Cherry=== Set 遍历 ===
20的平方: 400
40的平方: 1600
10的平方: 100
30的平方: 900=== Map 遍历 ===
MacBook 价格: ¥12999
iPhone 价格: ¥6999
iPad 价格: ¥3299

二.查看forEach源码

        将鼠标放置在idle的forEach()方法上,点击ctrl+B,就会跳转在forEach()方法的源码中,如下图所示:

三.源码分析

        我们将使用的方法的语句与定义forEach()方法的语句对比来看,定义forEach()方法的参数中有Consumer<? super T> action,那么这个就是我们对应的lambda表达式,那么其中的Consumer是什么?

答案就是一个接口,接口的内容如下所示:

        所以我们就是以函数式编程的方法完成了Consumer接口中的accept方法,然后在由forEach()方法去调用这个方法通过增强for的形式完成我们要执行的内容。

关键角色:Consumer 函数式接口

  • Consumer<T> 是一个函数式接口,定义了 accept(T t) 方法。

  • 使用 Lambda 或方法引用时,编译器会生成 Consumer 的实现:

list.forEach(element -> System.out.println(element));
// 等价于
list.forEach(new Consumer<>() {@Overridepublic void accept(String element) {System.out.println(element);}
});

以上的内容都是基于List完成,部分集合类重写了 forEach() 以提升性能

        使用`forEach()`方法与使用显式的迭代器(Iterator)或增强型for循环在性能上几乎没有差别,因为它们的底层实现都是迭代器。但是,在某些特定的集合实现中,如果覆盖了`forEach()`方法并进行了优化,可能会有微小差异。不过,在大多数情况下,这种差异可以忽略不计。

ArrayList:直接遍历数组,避免迭代器开销:

public void forEach(Consumer<? super E> action) {final E[] elementData = this.elementData;for (int i = 0; i < size; i++) {action.accept(elementData[i]);}
}

HashMap:遍历桶数组和链表/红黑树:

public void forEach(BiConsumer<? super K, ? super V> action) {Node<K,V>[] tab = table;for (Node<K,V> node : tab) {while (node != null) {action.accept(node.key, node.value);node = node.next;}}
}

        `forEach()`方法接受一个`Consumer`(对于Map是`BiConsumer`),所以我们可以使用Lambda表达式来传递行为。这使得代码更加简洁和易读。

四.与 Stream API 的关系

forEach() 也可用于 Stream

list.stream().forEach(System.out::println);

区别Stream.forEach() 不保证顺序(除非调用 forEachOrdered),而集合的 forEach() 按迭代顺序执行。

五.总结

关键点说明
底层机制基于迭代器模式遍历元素
函数式接口依赖 Consumer 接收操作逻辑
性能优化ArrayList/HashMap 等重写方法避免迭代器开销
并发修改直接修改集合会抛出 ConcurrentModificationException
与 Stream 区别集合的 forEach() 保证顺序;Stream.forEach() 不保证(并行流时明显)
http://www.lryc.cn/news/618444.html

相关文章:

  • 深度解读 WizTelemetry 2.0:链路追踪如何让分布式系统“无所遁形”
  • 【2025最新版】Java基础知识学习路线图:从入门到精通的系统化指南
  • 深度贴:前端网络基础及进阶(2)
  • 【网络运维】Linux和自动化: Ansible基础实践
  • 【接口自动化】-11-接口加密签名 全局设置封装
  • 回归预测 | Matlab实现CNN-BiLSTM-self-Attention多变量回归预测
  • 如何使用gpt进行模型微调?
  • iceberg FlinkSQL 特性
  • 古风修仙主题登录页面设计与实现 附源码 ~~~
  • Iptables 详细使用指南
  • 【CSS3】录音中。。。
  • 飞算JavaAI 2.0.0深度测评:自然语言编程如何重塑Java开发范式
  • 基于 gRPC 的接口设计、性能优化与生产实践
  • 《软件工程导论》实验报告一 软件工程文档
  • 新手向:Python编写简易翻译工具
  • Jmeter性能测试过程中遇到connection reset的解决方案
  • 易语言模拟真人鼠标轨迹算法 - 非贝塞尔曲线
  • HTTP应用层协议-长连接
  • 意图驱动——机器人大脑的正确驱动方式
  • 大模型驱动的服务革命:2025智能客服机器人选型与落地路径
  • 5-终端安全检测和防御技术
  • 【北京见闻】2025年世界机器人大会——所见所闻及所思
  • Node.js 精选:50 款文件处理与开发环境工具库
  • 最终章【1】Epson机器人篇
  • Ansible 自动化介绍
  • 什么时候用WS(WebSocket),什么使用用SSE(Server-Sent Events)?
  • windows git安装步骤
  • SSH浅析
  • Redis面试精讲 Day 19:Redis缓存设计模式与策略
  • 攻防世界—easyTornado