0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

为什么需要Streams?它们能取代Java中的for循环吗?

jf_ro2CN3Fa 来源:芋道源码 2023-11-03 09:39 次阅读

1 代码比较

具有类型的项目名称列表

生成随机列表

2 性能比较

迭代元素

并行流优化

3 局限性

条件循环

重复

4 概括

Java8的发布是Java历史上的一个重大时刻。Streams 和 Lambda 被引入,它们现在被广泛使用。如果你不知道 Streams,或者从来没有听说过它,那是完全没有问题的。在大多数情况下,循环同样可以满足我们的需要,没有 Streams 也不会遇到任何问题。

那我们为什么需要Streams?它们能取代循环吗?或者比循环更有优势?在本文中,我们将研究代码,比较性能,并看看Streams作为循环的替代品有多好。

1 代码比较

Streams 增加了代码复杂性,因为它们需要类、接口和导入新的包;相比之下,循环天生就是内置的,不需要额外的引入任何东西。这在某些点上是对的,但也不尽然:代码复杂度并不能仅仅看引入几个类、几个包文件来衡量,更重要的是代码的可读性。让我们看一些例子。

具有类型的项目名称列表

假设我们有一个项目列表,并且想要特定项目类型的名称列表。使用循环,我们需要编写以下内容:

ListgetItemNamesOfType(Listitems,Item.Typetype){
ListitemNames=newArrayList<>();
for(Itemitem:items){
if(item.type()==type){
itemNames.add(item.name());
}
}
returnitemNames;
}

阅读代码,我们会发现 ArrayList 应该实例化一个 new ,并且add()应该在每个循环中进行类型检查和调用。再来看,Streams 版本是如何处理的:

ListgetItemNamesOfTypeStream(Listitems,Item.Typetype){
returnitems.stream()
.filter(item->item.type()==type)
.map(item->item.name())
.toList();
}

在 Lambda 的帮助下,可以立即发现我们首先选择具有给定类型的项目,然后获取过滤项目的名称列表。在这种代码中,逐行流程与逻辑流程非常一致。

生成随机列表

让我们看另一个例子,在时间比较部分,我们将回顾关键的 Streams 方法并将它们的执行时间与循环进行比较。为此,我们需要一个随机的Items 列表。这是一个带有静态方法的片段,它给出了 随机 Item:

publicrecordItem(Typetype,Stringname){
publicenumType{
WEAPON,ARMOR,HELMET,GLOVES,BOOTS,
}

privatestaticfinalRandomrandom=newRandom();
privatestaticfinalString[]NAMES={
"beginner",
"knight",
"king",
"dragon",
};

publicstaticItemrandom(){
returnnewItem(
Type.values()[random.nextInt(Type.values().length)],
NAMES[random.nextInt(NAMES.length)]);
}
}

现在,让我们Item使用循环创建一个随机列表。代码如下所示:

Listitems=newArrayList<>(100);
for(inti=0;i< 100; i++) {
    items.add(Item.random());
}

Streams 的代码如下所示:

Listitems=Stream.generate(Item::random).limit(length).toList();

这是一段精彩且易于阅读的代码。此外,List返回的toList()方法中值是不可修改的,为我们提供了不变性,因此我们可以在代码中的任何位置共享它,而不必担心副作用。这使得代码不易出错,并且读者更容易理解我们的代码。

Streams 提供了多种有用的方法,让我们可以编写简洁的代码。最流行的是:

allMatch()

anyMatch()

count()

filter()

findFirst()

forEach()

map()

reduce()

sorted()

limit()

2 性能比较

在正常情况下,Streams 的行为类似于循环,对执行时间影响很小或没有影响。让我们将 Streams 中的一些主要行为与循环实现进行比较。

迭代元素

当我们有一个元素集合时,在很多情况下都会迭代集合中的所有元素。在 Streams 中,诸如forEach()、map()、reduce()和 filter()类的方法可以执行这种全元素迭代。

让我们考虑一种情况,我们想要对列表中的每种类型的项目进行计数。

带 for 循环的代码如下所示:

publicMaploop(Listitems){
Mapmap=newHashMap<>();
for(Itemitem:items){
map.compute(item.type(),(key,value)->{
if(value==null)return1;
returnvalue+1;
});
}
returnmap;
}

Streams 的代码如下所示:

publicMapstream(Listitems){
returnitems.stream().collect(Collectors.toMap(
Item::type,
value->1,
Integer::sum));
}

它们看起来截然不同,但它们的表现如何呢?下表是 100 次尝试的平均执行时间:

wKgZomVET7CAZUCWAAANg4Mn1PQ433.jpg

正如我们在上面的比较表中看到的,Streams 和循环在迭代整个列表时显示出很小的执行时间差异。在大多数情况下,这对于其他 Stream 方法(如map()、forEach()、reduce()等)是相同的。

并行流优化

因此,我们发现在迭代列表时,流的性能并不比循环更好或更差。然而,Streams 有一个循环所不具备的神奇之处:我们可以轻松地利用流进行多线程计算。 所要做的就是使用parallelStream()而不是stream()。

为了了解我们可以从中获得多少影响,让我们看一下下面的示例,其中我们模拟了耗时较长的任务,如下所示:

privatevoidlongTask(){
//Mocklongtask.
try{
Thread.sleep(1);
}catch(InterruptedExceptione){
thrownewRuntimeException(e);
}
}

循环遍历列表将如下所示:

protectedvoidloop(Listitems){
for(Itemitem:items){
longTask();
}
}

Stream将如下所示:

protectedvoidstream(Listitems){
items.stream().forEach(item->longTask());
}

最后,并行流将如下所示:

protectedvoidparallel(Listitems){
items.parallelStream().forEach(item->longTask());
}

请注意, onlystream()已更改为parallelStream()。

这是比较:

wKgaomVET8CAcP3fAAAz0_C8Mhk030.jpg

正如预期的那样,循环和Stream几乎没有什么区别。那么并行流呢?耸人听闻!与其他实现相比,它节省了 80% 以上的执行时间!这怎么可能?

对于需要很长时间才能完成并且应该为列表中的每个元素独立完成的任务,它们可以同时运行,我们可以期待显着的改进。这就是并行流正在做的事情。他们将它们分配到多个线程中并使它们同时运行。

并行流并非万能通用,只有当任务是独立的时,它才有用。如果任务不是独立的,并且必须共享相同的资源,则必须使用锁(主要是Java中的synchronized关键字)来保证它们的安全,此时它们的运行速度慢于正常的迭代。

3 局限性

然而,Stream也有局限性。一种情况是条件循环,另一种情况是重复。让我们看看它们的意思。

条件循环

当我们想要重复直到条件为真但不确定需要多少次迭代时,我们通常使用while循环。

booleancondition=true;
while(condition){
...
condition=doSomething();
}

使用 Streams 表现相同的代码如下所示:

Stream.iterate(true,condition->condition,condition->doSomething())
.forEach(unused->...);

我们可以看到 Streams 代码部分会干扰读取,例如condition -> condition检查条件是否为真,unused以及forEach()。考虑到这一点,条件循环最好写在while循环中。

重复

重复是for循环存在的主要原因之一。假设我们想重复这个过程十次。有了for循环,就可以很容易地写成:

for(inti=0;i< 10; i++) {
  ...
}

在 Streams 中,实现此目的的一种方法是创建IntStream包含[0, 1, 2, ... , 9]并迭代它的 。

IntStream.range(0,10).forEach(i->...);

虽然代码可能看起来简洁而正确,但它看起来更侧重于0到10(排除)范围内的值,其中for循环代码可以重复读取十次,因为更常见的做法是这样写repeat:从0开始,以重复次数结束。

4 概括

我们已经对流和循环进行了一些比较。那么……Streams 可以取代循环吗?嗯,一如既往,这取决于情况!然而,Streams 通常可以为我们提供更简洁、易于阅读的代码和优化。


来源:betterprogramming.pub/can-streams-replace-loops-in-java-f56d4461743a






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2904

    浏览量

    102994
  • for循环
    +关注

    关注

    0

    文章

    61

    浏览量

    2420

原文标题:Java 中的 Stream 可以替代 for 循环吗?

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Java入门需要学习什么?

    、怎样多线程需要理解机理?很多Java程序员热衷于多线程程序编写,认为是对逻辑能力的挑战。其实在大量应用根本就不需要编写多线程程序,或者说大多数编写应用程序的程序员不会去写多线程程序
    发表于 03-01 15:45

    Java的输入输出流盘点

    Java的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个):InputStream,OutputStream,Reader,Writer。Java
    发表于 07-11 07:56

    集成压控振荡器的宽带锁相环真的能取代分立式解决方案吗?

    输出信号;PLL监控输出信号并调谐VCO,以将其相对一个已知参考信号锁定。那么,集成压控振荡器的宽带锁相环真的能取代分立式解决方案吗?
    发表于 07-31 06:55

    怎么看待CKS32单片机能取代ST32单片机问题

    怎么看待中科芯CKS32单片机,能取代ST32单片机吗?你会选择CKS32的单片机吗?谢谢!
    发表于 08-22 15:03

    以CAN与RS485为例总结各自优势 解疑“为什么CAN能取代RS485”

    近年来CAN总线逐渐被工程师认知,并以其突出特点,逐渐在取代RS485等总线,本文将以CAN与RS485为例总结各自优势,为您解疑“为什么CAN能取代RS485”。
    的头像 发表于 01-15 14:58 3.5w次阅读
    以CAN与RS485为例总结各自优势 解疑“为什么CAN<b class='flag-5'>能取代</b>RS485”

    为什么CAN能取代RS485?

    分享到 近年来 CAN 总线逐渐被工程师认知,并以其突出特点,逐渐在取代RS485等总线,本文将以CAN与RS485为例总结各自优势,为您解疑“为什么CAN能取代RS485”。 现场总线是90年代初
    发表于 01-22 20:44 1037次阅读
    为什么CAN<b class='flag-5'>能取代</b>RS485?

    Java Map的几种循环方式学习总结

    本文档内容介绍了基于Java Map的几种循环方式学习总结,供参考
    发表于 03-19 15:51 0次下载

    对讲机和微信的区别在哪里?为什么微信,手机不能取代对讲机?

    对讲机和微信的区别在哪里?为什么微信,手机不能取代对讲机?
    的头像 发表于 07-19 16:04 9861次阅读

    AI不能取代医生的原因分析

    在相当长的时间内,人工智能只能服务医生,辅助医生,不可能取代医生,因为部分疾病需要根据病史和患者各个方面的具体情况来判断,这是AI做不到的。
    的头像 发表于 08-28 18:28 7071次阅读

    人工智能取代职业排行

    本视频主要详细介绍了人工智能取代职业排行,分别是电话推销员、打字员、会计、保险业务员、银行职员、政府职员、接线员、前台等。
    的头像 发表于 12-24 16:58 2.7w次阅读

    Java教程之如何进行Java中的do-while循环

    我们知道当一开始循环条件就不满足的时候,while循环一次也不会 执行。有的时候。我们有这样的需要:无论如何循环都先执行一次,再判断循环条件
    发表于 01-23 11:05 7次下载
    <b class='flag-5'>Java</b>教程之如何进行<b class='flag-5'>Java</b>中的do-while<b class='flag-5'>循环</b>

    Java循环语句的详细资料说明

    本文档的主要内容详细介绍的是Java循环语句的详细资料说明包括了:1、while循环语句,2、do…while循环语句,3、for循环语句
    发表于 03-22 08:00 0次下载
    <b class='flag-5'>Java</b>的<b class='flag-5'>循环</b>语句的详细资料说明

    机器人目前能取代人类记者和编辑吗?

    外媒称,有关微软公司本月开始用人工智能取代自家新闻网站部分编辑人员的新闻,一度成为网络上的关注热点。但专家认为,人工智能技术目前尚未发展到能够取代人类记者的水平。
    的头像 发表于 06-30 14:14 2209次阅读

    超级电容能当电池用吗?为什么超级电容不能取代电池

    超级电容能当电池用吗?为什么超级电容不能取代电池 超级电容器是一种非常有用的电子存储装置,可以存储大量的能量,并且具有很快的充放电速度和寿命长的特点。和电池不同,在充电和放电时,它们不会
    的头像 发表于 08-25 14:15 6420次阅读

    集成压控振荡器的宽带锁相环能取代分立式解决方案吗

    电子发烧友网站提供《集成压控振荡器的宽带锁相环能取代分立式解决方案吗.pdf》资料免费下载
    发表于 11-22 16:15 0次下载
    集成压控振荡器的宽带锁相环<b class='flag-5'>能取代</b>分立式解决方案吗