当前位置: 首页 > 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.xdnf.cn/news/1286101.html

相关文章:

  • 【Unity3D实例-功能-下蹲】角色下蹲(二)穿越隧道
  • 人工智能+虚拟仿真,助推医学检查技术理论与实践结合
  • Linux环境gitlab多种部署方式及具体使用
  • [论文阅读] (41)JISA24 物联网环境下基于少样本学习的攻击流量分类
  • 完整多端口 Nginx Docker部署 + GitLab Runner注册及标签使用指南
  • 使用 NetBird 创建安全的私有网络,简化远程连接!
  • 【论文阅读】从表面肌电信号中提取神经信息用于上肢假肢控制:新兴途径与挑战
  • 终端安全检测和防御技术总结
  • Java数据结构之ArrayList
  • [激光原理与应用-256]:理论 - 几何光学 - CMOS与CCD传感器成像原理比较
  • 卫生间装修防水怎么做合适?
  • 激光干涉法在碳化硅衬底 TTV 厚度测量中的精度提升策略
  • 高性能web服务器Tomcat
  • Vue 3 + Elementui + TypeScript 实现左侧菜单定位右侧内容
  • 石英加速度计如何实现高精度测量?
  • 深度贴:前端网络基础及进阶(3)
  • 鲲鹏arm服务器安装neo4j社区版,实现图书库自然语言检索基础
  • 地图可视化实践录:显示地理区域图
  • 自然语言处理关键库解析和使用方法- FuzzyWuzzy
  • 虚拟机一站式部署Claude Code 可视化UI界面
  • 豆包 + 蘑兔 AI:你的创作搭子
  • 运维学习Day22——Anisible自动化与基本使用
  • Kafka的一条消息的写入和读取过程原理介绍
  • kafka 消费者组的概念是什么?它是如何实现消息的点对点和发布/订阅模式?
  • PO、BO、VO、DTO、POJO、DAO、DO基本概念
  • 开源!!! htop移植到OpenHarmony
  • 【网络运维】Linux和自动化: Ansible基础实践
  • ncurses 6.5 交叉编译移植到OpenHarmomy
  • 【软考中级网络工程师】知识点之 IP QoS 技术
  • 小红书笔记信息获取_实在智能RPA源码解读