为什么不应该用Stream forEach替换for循环的3个原因

太棒了! 我们正在将代码库迁移到Java8。我们将用函数替换所有内容。 扔掉设计模式。 删除面向对象。 对! 我们走吧!

等一下

Java 8已经问世了一年多,而这种兴奋又回到了日常业务中。

baeldung.com从2015年5月开始执行的一项非代表性研究发现, 他们的读者中有38%已采用Java 8 。 在此之前,Typsafe在2014年末进行的一项研究声称,其用户中Java 8的采用率为27% 。

这对您的代码库意味着什么?

某些Java 7-> Java 8迁移重构是理所当然的。 例如,将Callable传递给ExecutorService

ExecutorService s = ...// Java 7 - meh...
Future<String> f = s.submit(new Callable<String>() {@Overridepublic String call() {return "Hello World";}}
);// Java 8 - of course!
Future<String> f = s.submit(() -> "Hello World");

匿名类样式实际上并没有在此添加任何值。

除了这些容易理解的话题之外,还有其他不太明显的话题。 例如,是否使用外部迭代器还是内部迭代器 。 另请参阅Neil Gafter于2007年发表的有关永恒主题的有趣读物: http ://gafter.blogspot.ch/2007/07/internal-versus-external-iterators.html

以下两个逻辑的结果相同

List<Integer> list = Arrays.asList(1, 2, 3);// Old school
for (Integer i : list)System.out.println(i);// "Modern"
list.forEach(System.out::println);

我主张“现代”方法应格外小心,即仅在您真正受益于内部功能迭代时(例如,通过Stream的map()flatMap()和其他操作链接一组操作map() ,才应格外小心。

与经典方法相比,这是“现代”方法的缺点的简短列表:

1.绩效–您将因此而蒙受损失

Angelika Langer在她的文章以及她在会议上发表的相关演讲中已经很好地总结了这个话题:

https://jaxenter.com/java-performance-tutorial-how-fast-are-the-java-8-streams-118830.html

在许多情况下,性能并不重要,因此您不应该进行任何过早的优化-因此您可能会声称此参数本身并不是真正的参数。 但是在这种情况下,我会反驳这种态度,说Stream.forEach()与普通的for循环相比的开销如此之大以至于默认情况下使用它只会在您的所有计算机上堆积很多无用的CPU周期应用。 如果仅根据循环样式的选择来谈论将CPU消耗提高10%-20%,那么我们所做的根本就是错误的。 是的–各个循环无关紧要,但是可以避免整个系统的负担。

这是Angelika在普通循环上的基准测试结果,在装箱的整数列表中找到最大值:

ArrayList, for-loop : 6.55 ms
ArrayList, seq. stream: 8.33 ms

在其他情况下,当我们对原始数据类型执行相对简单的计算时,我们绝对应该退回到经典的for循环(最好是数组而不是集合)。

这是Angelika在普通循环上的基准测试结果,在原始整数数组中找到最大值:

int-array, for-loop : 0.36 ms
int-array, seq. stream: 5.35 ms

过早的优化效果不好,但是避免过早优化的货运教育更加糟糕。 重要的是要反思我们所处的环境,并在这种环境下做出正确的决定。 我们之前已经写过关于性能的博客,请参阅我们的文章《 Java十大简单性能优化》。

2.可读性–至少对于大多数人而言

我们是软件工程师。 我们将始终讨论我们的代码样式,就像它真的很重要一样。 例如,空格或花括号。

我们这样做的原因是因为软件维护很困难。 特别是别人编写的代码。 很久以前。 在切换到Java之前谁可能只写了C代码。

当然,在到目前为止的示例中,我们确实没有可读性问题,这两个版本可能是等效的:

List<Integer> list = Arrays.asList(1, 2, 3);// Old school
for (Integer i : list)System.out.println(i);// "Modern"
list.forEach(System.out::println);

但是这里发生了什么:

List<Integer> list = Arrays.asList(1, 2, 3);// Old school
for (Integer i : list)for (int j = 0; j < i; j++)System.out.println(i * j);// "Modern"
list.forEach(i -> {IntStream.range(0, i).forEach(j -> {System.out.println(i * j);});
});

事情开始变得更加有趣和异常。 我不是说“更糟”。 这是实践和习惯的问题。 并且没有黑/白问题的答案。 但是,如果其余代码库是必须的(可能是这样),则嵌套范围声明和forEach()调用以及lambda肯定是不寻常的,这会在团队中引起认知冲突 。

您可以构建一些示例,其中命令式方法比等效的功能式方法感觉更尴尬,如此处所示:

但是在许多情况下,这是不正确的,并且编写功能上相对简单的命令相当的功能相当困难(同样,效率低下)。 可以在此博客的先前文章中看到一个示例: http : //blog.jooq.org/2015/09/09/how-to-use-java-8-functional-programming-to-generate-an-alphabetic -序列/

在那篇文章中,我们生成了一个字符序列:

A, B, ..., Z, AA, AB, ..., ZZ, AAA

…类似于MS Excel中的列:

图1_50047

命令式方法( 最初由Stack Overflow上的一个未命名用户使用 ):

import static java.lang.Math.*;private static String getString(int n) {char[] buf = new char[(int) floor(log(25 * (n + 1)) / log(26))];for (int i = buf.length - 1; i >= 0; i--) {n--;buf[i] = (char) ('A' + n % 26);n /= 26;}return new String(buf);
}

……在简洁的层次上可能胜过功能性的:

import java.util.List;import org.jooq.lambda.Seq;public class Test {public static void main(String[] args) {int max = 3;List<String> alphabet = Seq.rangeClosed('A', 'Z').map(Object::toString).toList();Seq.rangeClosed(1, max).flatMap(length ->Seq.rangeClosed(1, length - 1).foldLeft(Seq.seq(alphabet), (s, i) -> s.crossJoin(Seq.seq(alphabet)).map(t -> t.v1 + t.v2))).forEach(System.out::println);}
}

并且它已经在使用jOOλ来简化编写功能性Java的工作。

3.可维护性

让我们再次考虑前面的示例。 现在,我们不对值进行相乘,而是对它们进行除法。

List<Integer> list = Arrays.asList(1, 2, 3);// Old school
for (Integer i : list)for (int j = 0; j < i; j++)System.out.println(i / j);// "Modern"
list.forEach(i -> {IntStream.range(0, i).forEach(j -> {System.out.println(i / j);});
});

显然,这是自找麻烦,我们可以在异常堆栈跟踪中立即看到问题。

老套

Exception in thread "main" java.lang.ArithmeticException: / by zeroat Test.main(Test.java:13)

现代

Exception in thread "main" java.lang.ArithmeticException: / by zeroat Test.lambda$1(Test.java:18)at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)at java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:557)at Test.lambda$0(Test.java:17)at java.util.Arrays$ArrayList.forEach(Arrays.java:3880)at Test.main(Test.java:16)

哇。 我们只是…吗? 是。 这就是为什么我们首先在项目1中遇到性能问题的原因。 对于JVM和库而言,内部迭代只是要做更多的工作。 这是一个非常简单的用例,我们可以用AA, AB, .., ZZ系列的产品展示同样的东西。

从维护的角度来看,函数式编程风格比命令式编程要困难得多,尤其是当您在传统代码中盲目地将两种风格混合在一起时。

结论

这通常是一个关于函数式编程,关于声明式编程的博客。 我们喜欢lambda。 我们喜欢SQL。 结合起来,它们可以创造奇迹 。

但是,当您迁移到Java 8并考虑在代码中使用更多功能的样式时,请注意FP并不总是总会更好–出于各种原因。 实际上,它从来没有“更好”,它只是不同而已,它使我们对问题有不同的推理。

我们的Java开发人员将需要进行练习,并且对何时使用FP以及何时坚持使用OO /命令式有一个直观的了解。 通过适当的实践,将两者结合起来将有助于我们改进软件。

或者,用鲍伯叔叔的话来说:

底线就是这个。 当您知道OO编程是什么时,它就是好的。 当您知道函数式编程是什么时,它就是好的。 一旦了解了功能性OO编程,它也是不错的选择。

http://blog.cleancoder.com/uncle-bob/2014/11/24/FPvsOO.html

翻译自: https://www.javacodegeeks.com/2015/12/3-reasons-shouldnt-replace-loops-stream-foreach.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/355510.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用NPOI库导入导出EXCEL

一、EXCEL 导入&#xff08;Excel 导入导出实际项目中会被封装成**Helper 本示例只对简单功能做演示&#xff09; NPOI 包引用 视图view {ViewBag.Title "NPOIExcel"; }<h2>NPOIExcel</h2> <form action"Url.Action("NPOIInport", &…

oracle12c ora 12547,Oracle 12c DBCA出现PRCR-1079 ORA-12547 CRS-5017

Oracle 12c用dbca创建数据库时出现了PRCR-1079 ORA-12547 CRS-5017不能启动数据库。因为这里安装了Oracle Restart&#xff0c;所以尝试使用srvctl start database命令来手动启动数据库&#xff0c;但是结果还是一样不能启动。[gridoracle12c 12.1]$ srvctl start database -db…

kmeans鸢尾花分类python代码_python实现鸢尾花三种聚类算法(K-means,AGNES,DBScan)

一.分散性聚类(kmeans) 算法流程: 1.选择聚类的个数k. 2.任意产生k个聚类&#xff0c;然后确定聚类中心&#xff0c;或者直接生成k个中心。 3.对每个点确定其聚类中心点。 4.再计算其聚类新中心。 5.重复以上步骤直到满足收敛要求。&#xff08;通常就是确定的中心点不再改变。…

akka_Akka的字数统计MapReduce

akka在我与Akka的日常工作中&#xff0c;我最近写了一个字数映射减少示例。 本示例实现了Map Reduce模型&#xff0c;该模型非常适合横向扩展设计方法。 流 客户端系统&#xff08;FileReadActor&#xff09;读取文本文件&#xff0c;并将每一行文本作为消息发送给ClientActor…

Log4j 2配置与IntelliJ IDEA控制台颜色

Log4j是Java平台上最好的日志组件了&#xff0c;Log4j 2升级了不少API&#xff0c;拓展性更好。使用的话只需要直接引入就可以了. <dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version> &…

用c3p0连接oracle怎么分页,JSP分页(MySql+c3p0+dbutils)

JSP分页(MySqlc3p0dbutils)来源&#xff1a;互联网作者&#xff1a;佚名时间&#xff1a;2015-02-16 17:27为什么要对数据进行分页&#xff1f;当数据较多时&#xff0c;页面就会变的很庞大&#xff0c;不仅会影响到用户的使用&#xff0c;而且还有加重服务器的负担。下面简单的…

Apache Storm的实时情绪分析示例

实时情感分析是指处理自然语言文本&#xff08;或语音&#xff09;流以提取主观信息。 琐碎的用例用于构建推荐引擎或查找社交媒体趋势。 我选择了Apache Storm作为实时处理引擎。 Storm非常强大&#xff08;我们正在生产中使用它&#xff09;&#xff0c;并且非常容易在其之上…

flink sql设置并行度_《从0到1学习Flink》—— Flink parallelism 和 Slot 介绍

前言之所以写这个是因为前段时间自己的项目出现过这样的一个问题&#xff1a;Caused by: akka.pattern.AskTimeoutException: Ask timed out on [Actor[akka://flink/user/taskmanager_0#15608456]] after [10000 ms]. Sender[null] sent message of type "org.apache.f…

zabbix3.2监控

自动化运维框架 运维标准流程监控管理容量管理、关联关系、任务管理、自动部署、分布式集群、传统集群、机器管理安全控制灾难管理 自动化监控 监控评估数据采集主动式数据采集: client、公共插件、自定义脚本被动式服务状态: 服务状态、程序状态、用户访问质量第三方信息 公…

fasttext 文本分类_一文综述经典的深度文本分类方法

作者 | 何从庆转载自AI算法之心(ID:AIHeartForYou)笔者整理最近几年比较经典的深度文本分类方法&#xff0c;希望帮助小伙伴们了解深度学习在文本分类中的应用。Convolutional Neural Networks for Sentence Classification (EMNLP 2014)Kim在EMNLP2014提出的TextCNN方法&…

vi/vim 编辑器详解

vi/vim &#xff1a; 强大的编辑器 进入vi的命令 vi filename :打开或新建文件&#xff0c;并将光标置于第一行首 vi n filename &#xff1a;打开文件&#xff0c;并将光标置于第n行首 vi filename &#xff1a;打开文件&#xff0c;并将光标置于最后一行首 vi /pattern …

实验三+067+冯艳芳

一、实验目的 掌握黑盒测试用例设计方法 二、实验要求 &#xff08;1&#xff09;对被测程序进行黑盒测试用例设计 &#xff08;2&#xff09;运用等价类、边界值、决策表、状态图法等进行测试用例设计。 &#xff08;3&#xff09;对手机上任意一款音乐软件进行黑盒测试实践。…

python越来越慢_为什么Python中的串联速度越来越慢?

为什么在某些情况下,Python 3中的连接似乎比Python 2中的连接慢&#xff1f; 影响最大的串联方法似乎是字节对象的连续串联,从O(n)到O(n?)操作. 我的分析代码大部分在这里&#xff1a; #!/usr/bin/env python from operator import concat from sys import version, version_i…

jvm gc策略_IBM JVM调整– gencon GC策略

jvm gc策略本文将向您详细介绍从Java虚拟机&#xff08;例如HotSpot或JRockit&#xff09;迁移到IBM JVM时重要的Java堆空间调整注意事项。 此调整建议基于我为我的一个IT客户端执行的最新故障排除和调整任务。 IBM JVM概述 正如您可能从其他文章中看到的那样&#xff0c;IBM …

芬兰高性能图表控件-免费试用并提供技术支持

图表控件对于很多技术研发人员、工程设计师来说肯定不陌生&#xff0c;但市面上已有的图表控件产品大多功能单一、性能也不稳定&#xff0c;很难满足不同人群在不同场合的使用需求。为此&#xff0c;专注于开发高性能和最先进的数据可视化工具公司Arction则给出了完美的解决方案…

linux open函数_Linux驱动开发 / 字符设备驱动内幕 (1)

哈喽&#xff0c;我是老吴&#xff0c;继续记录我的学习心得。一、保持专注的几个技巧将最重要的事放在早上做。待在无干扰环境下&#xff0c;比如图书馆。意识到刚坐下开始投入工作前&#xff0c;有点负面小情绪是特别正常的现象。让“开心一刻”成为计划的一部分。拥有合情合…

github删除文件_github 仓库中删除历史大文件

问题如果git中提交了大文件&#xff0c;而且保存到了版本库中&#xff0c;那在下载或者克隆git包的时候&#xff0c;速度会非常慢。再加上github在国内访问本来就很慢&#xff0c;可能会导致包无法下载(克隆)。为了提升下载(克隆)速度&#xff0c;可以永久的删除这些文件(包括该…

linux plc编程软件,基于Linux平台的可编程控制器软PLC设计

实例下面以一个简单的对3并口通道循环控制为例&#xff0c;说明软PLC 的工作流程。(1)梯形图编程。从软PLC 主界面进入后&#xff0c;启动梯形图编程&#xff0c;调用梯形图编程的主程序。梯形图编程共需要调用梯形图界面模块、关闭模块、IO 模块&#xff0c;这些均在配置文件中…

insert into语句_入门MySQL——DML语句篇

前言&#xff1a;在上篇文章中&#xff0c;主要为大家介绍的是DDL语句的用法&#xff0c;可能细心的同学已经发现了。本篇文章将主要聚焦于DML语句&#xff0c;为大家讲解表数据相关操作。这里说明下DDL与DML语句的分类&#xff0c;可能有的同学还不太清楚。DDL(Data Definitio…

linux不重启换root密码是什么原因,在Linux下修改和重置root密码的方法(超简单)

刚开始接触linux的人&#xff0c;忘记了root密码可能会不知所措。想找回自己的root密码&#xff0c;但是又不知道方法。其实&#xff0c;只需要简单的几步就可以重置自己的root密码了(找回密码我也不会)1.开机HcQBEm上敲击e&#xff0c;然后编辑选项2.在linux16这一行&#xff…