Java函数式编程(四)集合的使用

本系列文章译自Venkat Subramaniam的[url=http://it.deepinmind.com][i]Functional Programming in Java[/i][/url]

[b]第二章:集合的使用[/b]


我们经常会用到各种集合,数字的,字符串的还有对象的。它们无处不在,哪怕操作集合的代码要能稍微优化一点,都能让代码清晰很多。在这章中,我们探索下如何使用lambda表达式来操作集合。我们用它来遍历集合,把集合转化成新的集合,从集合中删除元素,把集合进行合并。

[b]遍历列表[/b]

遍历列表是最基本的一个集合操作,这么多年来,它的操作也发生了一些变化。我们使用一个遍历名字的小例子,从最古老的版本介绍到现在最优雅的版本。

用下面的代码我们很容易创建一个不可变的名字的列表:

final List<String> friends =
Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott");
System.out.println(friends.get(i));
}


下面这是最常见的一种遍历列表并打印的方法,虽然也最一般:


for(int i = 0; i < friends.size(); i++) {
System.out.println(friends.get(i));
}



我把这种方式叫做自虐型写法——又啰嗦又容易出错。我们得停下来好好想想,"是i<还是i<=呢?"这只有当我们需要操作具体某个元素的时候才有意义,不过即便这样,我们还可以使用坚持不可变原则的函数式风格来实现,这个我们很快会讨论到。

Java还提供了一种相对先进的for结构。

collections/fpij/Iteration.java
for(String name : friends) {
System.out.println(name);
}


在底层,这种方式的迭代是使用Iterator接口来实现的,调用了它的hasNext和next方法。
这两种方式都属于外部迭代器,它们把如何做和想做什么揉到了一起。我们显式的控制迭代,告诉它从哪开始到哪结束;第二个版本则在底层通过Iterator的方法来做这些。显式的操作下,还可以用break和continue语句来控制迭代。
第二个版本比第一个少了点东西。如果我们不打算修改集合的某个元素的话,它的方式比第一个要好。不过这两种方式都是命令式的,在现在的Java中应该摒弃这种方式。
改成函数式原因有这几个:


for循环本身是串行的,很难进行并行化。
这样的循环是非多态的;所得即所求。我们直接把集合传给for循环,而不是在集合上调用一个方法(支持多态)来执行特定的操作。
从设计层面来说,这样 写的代码违反了“Tell,Don't Ask”的原则 。我们请求执行一次迭代,而不是把迭代留给底层库来执行。


是时候从老的命令式编程转换到更优雅的内部迭代器的函数式编程了。使用内部迭代器后我们把很多具体操作都扔给了底层方法库来执行,你可以更专注于具体的业务需求。底层的函数会负责进行迭代的。我们先用一个内部迭代器来枚举一下名字列表。

Iterable接口在JDK8中得到加强,它有一个专门的名字叫forEach,它接收一个Comsumer类型的参数。如名字所说,Consumer的实例正是通过它的accept方法消费传递给它的对象的。我们用一个很熟悉的匿名内部类的语法来使用下这个forEach方法:

friends.forEach(new Consumer<String>() { public void accept(final String name) {
System.out.println(name); }
});



我们调用了friends集合上的forEach方法,给它传递了一个Consumer的匿名实现。这个forEach方法从对集合中的每一个元素调用传入的Consumer的accept方法,让它来处理这个元素。在这个示例中我们只是打印了一下它的值,也就是这个名字。
我们来看下这个版本的输出结果,和上两个的结果 是一样的:


Brian
Nate
Neal
Raju
Sara
Scott


我们只改了一个地方:我们抛弃了过时的 for循环,使用了新的内部迭代器。好处是,我们不用指定如何迭代这个集合,可以更专注于如何处理每一个元素。缺点是,代码看起来更啰嗦了——这简直要把新的编码风格带来的喜悦冲的一干二净了。所幸的是,这个很容易改掉,这正是lambda表达式和新的编译器的威力大展身手的时候了。我们再做一点修改,把匿名内部类换成lambda表达式。


friends.forEach((final String name) -> System.out.println(name));


这样看起来就好多了。代码更少了,不过我们先来看下这是什么意思。这个forEach方法是一个高阶函数,它接收一个lambda表达式或者代码块,来对列表中的元素进行操作。在每次调用的时候 ,集合中的元素会绑定到name这个变量上。底层库托管了lambda表达式调用的活。它可以决定延迟表达式的执行,如果合适的话还可以进行并行计算。
这个版本的输出也和前面的一样。

Brian
Nate
Neal
Raju
Sara
Scott


内部迭代器的版本更为简洁。而且,使用它的话我们可以更专注每个元素的处理操作,而不是怎么去遍历——这可是声明式的。

不过这个版本还有缺陷。一旦forEach方法开始执行了,不像别的两个版本,我们没法跳出这个迭代。(当然有别的方法能搞定这个)。因此,这种写法在需要对集合里的每个元素处理的时候比较常用。后面我们会介绍到一些别的函数可以让我们控制循环的过程。

lambda表达式的标准语法,是把参数放到()里面,提供类型信息并使用逗号分隔参数。Java编译器为了解放我们,还能自动进行类型推导。不写类型当然更方便了,工作少了,世界也清静了。下面是上一个版本去掉了参数类型之后的:


friends.forEach((name) -> System.out.println(name));


在这个例子里,Java编译器通过上下文分析,知道name的类型是String。它查看被调用方法forEach的签名,然后分析参数里的这个函数式接口。接着它会分析这个接口里的抽象方法,查看参数的个数及类型。即便这个lambda表达式接收多个参数,我们也一样能进行类型推导,不过这样的话所有参数都不能带参数类型;在lambda表达式中,参数类型要么全不写,要写的话就得全写。

Java编译器对单个参数的lambda表达式会进行特殊处理:如果你想进行类型推导的话,参数两边的括号可以省略掉。


friends.forEach(name -> System.out.println(name));


这里有一点小警告:进行类型推导的参数不是final类型的。在前面显式声明类型例子中,我们同时也把参数标记为final的。这样能防止你在lambda表达式中修改参数的值。通常来说,修改参数的值是个坏习惯,这样容易引起BUG,因此标记成final是个好习惯。不幸的是,如果我们想使用类型推导的话,我们就得自己遵守规则不要修改参数,因为编译器可不再为我们保驾护航了。

走到这步可费了老劲了,现在代码量确实少了一点。不过这还不算最简。我们来体验下最后这个极简版的。

friends.forEach(System.out::println);


在上面的代码中我们用到了一个方法引用。我们用方法名就可以直接替换整个的代码了。在下节中我们会深入探讨下这个,不过现在我们先来回忆下Antoine de Saint-Exupéry的一句名言:完美不是无法再增添加什么,而是无法再去掉什么。

lambda表达式让我们能够简洁明了的进行集合的遍历。下一节我们会讲到它如何使我们在进行删除操作和集合转化的时候,也能够写出如此简洁的代码。


未完待续,后续文章请继续关注[url=http://it.deepinmind.com]deepinmind[/url]。


原创文章转载请注明出处:[url=http://it.deepinmind.com]http://it.deepinmind.com[/url]
Java 中的函数式编程探索 Java自Java8起通过引入Lambda表达式、函数接口和StreamsAPI,开始支持函数式编程(FP),尽管其设计根植于1990年代的硬件限制,导致在实现FP时面临挑战。Java的默认可变性与FP的不可变性原则相冲突,且其类型系统和集合设计对FP构成限制。然而,Java通过高阶函数、递归、闭包、惰性求值和柯里化等技术 阅读详情

相关推荐

三极管稳压电路仿真分析

Z1的负极连接到三极管的基极,三极管发射极E是输出,形成了一个射极跟随器,也就是说,E极的电压随着B电压变化而变化,但BE之间有一个压降,一般硅管0.7V左右,假设B极是6.7V,那E极就是6.7-0.7=6V,所以就有一个公式VOUT=Vz-Vbe,可以根据VOUT来选择合适的稳压管。从稳压管的SPEC中我们可以看到,Vz@Izt这个参数,指在Izt=20mA时,稳压管的稳压值Vz=5.1V,也就是说流过稳压管的电流在20mA以上,稳压效果最佳。三极管导通之后,E点电压会升高,当升高到Vbe。

U_YAO的博客 2323

Java SE 8新功能介绍:Lambda的遍历,过滤,处理集合和方法增强

在“ Java SE 8新功能导览”系列的这篇文章中,我们将深入解释并探索代码,以了解如何使用lambda表达式和方法引用 遍历集合 ,并使用谓词接口过滤它们,实现默认方法在接口中,最后在接口中实现静态方法 。 在上一篇文章“ 使用Lambda Expression进行函数式编程 ”中; 我已深入了解lambda表达式。 我向您展示了Lambda表达式的几种不同用法。 它们都有功能...

最佳 Java 编程 1086

深入解析Zynq Ultrascale+ RF Data Converter中的PLL配置与时钟管理

本文深入解析了Zynq Ultrascale+ RF Data Converter中PLL配置与时钟管理的核心原理与实践。文章将RFDC时钟系统比作交响乐团,详细阐述了PLL作为指挥的关键作用,以及系统时钟、Tile时钟、参考时钟与采样时钟的层级关系。重点剖析了PLL配置参数(如VCO频率、分频器设置)的计算方法与稳定性考量,并提供了从IP配置使能顺序到多Tile同步、DRP访问等常见问题的实战避坑指南与调试技巧,帮助工程师构建稳定的射频信号处理基石。

7up55的博客 449

lambda遍历list对象

@Data public class SchoolBoy { private String girlId; private String id; private String name; private String grilName; } public static void main(String[] args) { List<SchoolBoy> schoolBoys = new ArrayList<>(); ...

pyd1040201698的博客 1343

react +ant desiagn pro实现按钮操作权限控制

在ant desiagn pro中提供了一个access用于对权限进行控制,需引入useAccess和Access

Sylvasylva的博客 2211

避坑指南:QCustomPlot鼠标事件冲突解决方案(左键拖动vs右键框选)

本文深入解析QCustomPlot在QT框架下的高级交互实现,重点解决左键拖动与右键框选的冲突问题。通过自定义事件处理和QRubberBand技术,提供了一套完整的无冲突交互方案,帮助开发者优化数据可视化应用的用户体验。

moss5的博客 660

lambda遍历集合

遍历List List<Strudent> list = new ArrayList<Strudent>() {{ add(new Strudent("b", 11, "女")); add(new Strudent("a", 19, "男")); add(new Strudent("d", 15, "女...

风华流沙君莫笑的博客 1819

硅基半导体量子点:单电子自旋量子比特技术手册

现在我们已经了解了从硅片到容错计算的全部环节,让我们用一张总览图把碎片拼成完整拼图。fill:#333;important;important;fill:none;color:#333;color:#333;important;fill:none;fill:#333;height:1em;important;important;important;color:#000!important;important;important;important;important;color:#000!

VectorShift的博客 371

使用Lambda表达式遍历集合

Iterable.forEach(Consumer action)是在Java 8中新增的方法,Consumer类型是函数式,所以传参时可以用Lambda表达式,同时Iterable是Collection的父接口,所以Collection及其子实现接口都可以使用此方法。在使用forEach函数遍历集合元素时,会将元素依次传给Consumer的accept(T t)(该接口中唯一的抽象方法)方法,所以使用此函数可以非常方便的遍历集合元素。

coderflyer 1493

【SpringBoot 3.x 第51节】带你玩转 GraalVM Native Image:启动快到飞起,内存省到心疼,你还不试试?

🏆本文收录于《滚雪球学SpringBoot 3》,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。    本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】,你想学习的都被收集在内,快速投入学习!!两不误。    若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》,涵盖SpringBoot所有版本教学文章

**My Coding Family** 1304

【WRF数据准备】土地利用数据-MODIS MCD12Q1数据下载及制备

【WRF数据准备】MODIS静态地理数据下载及制备

WW、forever的博客 3413

Lambda表达式遍历List集合

import java.util.ArrayList; import java.util.List; public class Lambda { public static void main(String[] args) { List list = new ArrayList<>(); list.add(“zhagnsan”); list.add(“wangwu”); list.ad...

weixin_43932564的博客 7236

2025年GESP12月认证C++四级真题解析

摘要 本文包含11道C++编程题目,涵盖指针、数组、函数参数传递、结构体和排序算法等知识点。每题2分,总分30分,主要考察学生对C++基础语法的掌握程度。题目包括指针定义与解引用(第1-4题)、二维/三维数组遍历(第4-5题)、变量作用域(第6题)、函数传参方式(第7题)、结构体操作(第8题)、递归算法(第9题)和排序稳定性判断(第10题)。最后第11题要求补全选择排序算法代码片段。每道题均提供标准答案和详细解析,适合用于检验C++基础知识的掌握情况。

lan_in的博客 317

java 集合函数编程_Java函数式编程(四)集合的使用

第二章:集合的使用我们经常会用到各种集合,数字的,字符串的还有对象的。它们无处不在,哪怕操作集合的代码要能稍微优化一点,都能让代码清晰很多。在这章中,我们探索下如何使用lambda表达式来操作集合。我们用它来遍历集合,把集合转化成新的集合,从集合中删除元素,把集合进行合并。遍历列表遍历列表是最基本的一个集合操作,这么多年来,它的操作也发生了一些变化。我们使用一个遍历名字的小例子,从最古老的版本介绍...

weixin_39929153的博客 141

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Java for函数用法_Java函数式编程(四)集合的使用

第二章:集合的使用我们经常会用到各种集合,数字的,字符串的还有对象的。它们无处不在,哪怕操作集合的代码要能稍微优化一点,都能让代码清晰很多。在这章中,我们探索下如何使用lambda表达式来操作集合。我们用它来遍历集合,把集合转化成新的集合,从集合中删除元素,把集合进行合并。遍历列表遍历列表是最基本的一个集合操作,这么多年来,它的操作也发生了一些变化。我们使用一个遍历名字的小例子,从最古老的版本介绍...

weixin_36184940的博客 394

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

java实验四 集合与函数式编程实验

(2020.4.28版) nefu2020.4.28版,刚刚交mainStudentServiceImplDatabaseUtilsStudentServiceStudent 一、实验目的: 掌握集合的基本操作方法 掌握Lambda表达式的声明编写规范 掌握基于集合Stream的过滤/映射/聚合等操作 掌握基于函数式编程集合元素的移除方法 二、实验内容: 初始化 理解实验的目的,不要浪费时间在初...

weixin_44382711的博客 1790

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

【Java系列】函数式接口编程

在 Java 中,函数接口通常指的是函数式接口(Functional Interface)。函数式接口是 Java 8 引入的一个特性,它是一个只包含一个抽象方法的接口。函数式接口的主要目的是支持 Lambda 表达式和方法引用,使得函数式编程更加容易和方便。函数式接口有一个特殊的注解,用来标识该接口是一个函数式接口。如果一个接口使用了注解,并且不满足函数式接口的条件(拥有多个抽象方法),编译器将会报错。在上面的示例中,MyFunction是一个函数式接口,因为它只有一个抽象方法apply。

优快云站内信: https://i.youkuaiyun.com/#/msg/chat/qyj19920704 1万+

Java函数式编程

Java函数式编程 初探函数式编程【JavaScript篇】哔哩哔哩bilibili 三更草堂Up主。不会Lambda表达式、函数式编程?你确定能看懂公司代码?-java8函数式编程(Lambda表达式,Optional,Stream流)从入门到精通-最通俗易懂_哔哩哔哩_bilibili 项目地址:bearbrick0/FuncProgram: 👀Java函数式编程的demo (github.com) 什么是函数式编程 为什么要学习函数式编程

- 7129

Java函数式编程入门

函数式编程在Java中不仅能提高代码的简洁性和可读性,还能在处理数据流、并行计算等场景中发挥重要作用。通过掌握Lambda表达式、Stream API、Optional以及其他相关技术,并结合实际业务场景的应用,你将能够更高效地开发出安全、稳定和可维护的系统。建议在日常项目中逐步引入函数式编程思想,从简单的集合操作开始,逐步探索更多高级特性,并结合代码评审和测试不断完善实践经验。/*** 函数式编程*//*** Function<T, R>:接受T类型的参数,返回R类型的结果。

zjh285077371的专栏 1779

Java 中的函数式编程详解

摘要 Java 8 引入函数式编程特性,包括 Lambda 表达式、函数式接口、方法引用和 Stream API,使代码更简洁高效。函数式接口(如 Function、Consumer)是基础,Lambda 表达式简化匿名函数写法,方法引用进一步优化语法。Stream API 支持链式数据操作,结合过滤、映射等中间操作和收集等终端操作,提升数据处理能力。Optional 类帮助避免空指针异常。函数式编程优势包括代码简洁、可读性强和并行处理能力,但也存在性能开销和学习成本。实战示例展示了如何用 Stream 处

2503_92145588的博客 1904

java函数式编程

函数式接口是只包含一个抽象方法的接口(不包括Object类中的方法)。这样的接口可以被隐式地转换为Lambda表达式。Stream API 提供了一种高效且易于使用的方式来处理集合(Collection)和数组。Stream操作分为中间操作(Intermediate Operations)和终端操作(Terminal Operations)。方法引用是Lambda表达式的一种简洁写法,它允许你直接引用已存在的方法或构造器。

2401_83447580的博客 1563

【Java】Java8重要特性——Lambda函数式编程以及Stream流对集合数据的操作

Java 8引入了Lambda函数式编程和Stream流这两个重要特性。Lambda表达式简化了匿名内部类的使用,提高了代码的表达能力和可读性。Stream流提供了便捷高效的方式对集合数据进行操作,支持延迟执行、函数式编程和并行处理,可以对数据进行过滤、映射、排序、聚合等操作。这两个特性的引入使得Java编程更加灵活高效,极大地提升了开发效率和代码质量。Lambda函数式编程和Stream流已经成为Java编程中不可或缺的重要特性,广泛应用于集合操作、并行处理等场景。

peng_YuJun的博客 1329
上一篇: ThreadSafe:诊断并发问题的利器
下一篇: Java函数式编程(四)集合的使用
spidercoco
博客等级 码龄18年 47粉丝 0原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值