当前位置: 首页 > 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.dtcms.com/a/328285.html

相关文章:

  • 数据科学与计算:爬虫和数据分析案例笔记
  • 01数据结构-Kruskal算法
  • 破译真实感:渲染参数进阶指南——告别塑料感,唤醒材质生命力
  • 01. maven的下载与配置
  • ubuntu24下keychorn键盘连接不了的改建页面的问题修复
  • “生成式UI革命”:Tambo AI如何让你的应用“开口说话、动手搭界面” | 全面深剖、案例实践与未来展望
  • Seed-VC:零样本语音转换与扩散transformer
  • 08--深入解析C++ list:高效操作与实现原理
  • 从爬虫新手到DrissionPage实践者的技术旅程
  • 【IP查询】使用IP66(ip66.net)验证IP地址定位的准确率
  • 小智智能交互算法通过国家备案,视觉大模型引领AI应用新浪潮
  • 机器学习之TF-IDF文本关键词提取
  • 终端安全检测与防御技术
  • 数据结构:中缀到后缀的转换(Infix to Postfix Conversion)
  • 【速通版!语义通信基础与前沿学习计划】
  • C++中类之间的关系详解
  • AR巡检:三大核心技术保障数据准确性
  • Langchain入门:构建一个PDF摄取和问答系统
  • 51 单片机分层架构的模块依赖关系图
  • 解决ROS编译顺序不对,需要内部依赖,因此要多次编译的问题
  • Python初学者笔记第二十二期 -- (JSON数据解析)
  • MySQL 数据库表操作与查询实战案例
  • 双十一美妆数据分析:洞察消费趋势与行业秘密
  • 机械臂的智能升维:当传统机械臂遇见Deepoc具身智能大模型从自动化工具到具身智能体的范式革命
  • Element用法---Loading 加载
  • C++的异常的使用和规范
  • 【盘古100Pro+开发板实验例程】FPGA学习 | 均值滤波 | 图像实验指导手册
  • 【代码随想录day 18】 力扣 501.二叉搜索树中的众数
  • 免费播客翻译与转录:用中文收听全球播客
  • Langchain入门:文本摘要