1 代码比较
具有类型的项目名称列表
生成随机列表
2 性能比较
迭代元素
并行流优化
3 局限性
条件循环
重复
4 概括
java8的发布是java历史上的一个重大时刻。streams 和 lambda 被引入,它们现在被广泛使用。如果你不知道 streams,或者从来没有听说过它,那是完全没有问题的。在大多数情况下,循环同样可以满足我们的需要,没有 streams 也不会遇到任何问题。
那我们为什么需要streams?它们能取代循环吗?或者比循环更有优势?在本文中,我们将研究代码,比较性能,并看看streams作为循环的替代品有多好。
1 代码比较
streams 增加了代码复杂性,因为它们需要类、接口和导入新的包;相比之下,循环天生就是内置的,不需要额外的引入任何东西。这在某些点上是对的,但也不尽然:代码复杂度并不能仅仅看引入几个类、几个包文件来衡量,更重要的是代码的可读性。让我们看一些例子。
具有类型的项目名称列表
假设我们有一个项目列表,并且想要特定项目类型的名称列表。使用循环,我们需要编写以下内容:
list getitemnamesoftype(list items, item.type type) { list itemnames = new arraylist(); for (item item : items) { if (item.type() == type) { itemnames.add(item.name()); } } return itemnames;}
阅读代码,我们会发现 arraylist 应该实例化一个 new ,并且add()应该在每个循环中进行类型检查和调用。再来看,streams 版本是如何处理的:
list getitemnamesoftypestream(list items, item.type type) { return items.stream() .filter(item -> item.type() == type) .map(item -> item.name()) .tolist();}
在 lambda 的帮助下,可以立即发现我们首先选择具有给定类型的项目,然后获取过滤项目的名称列表。在这种代码中,逐行流程与逻辑流程非常一致。
生成随机列表
让我们看另一个例子,在时间比较部分,我们将回顾关键的 streams 方法并将它们的执行时间与循环进行比较。为此,我们需要一个随机的items 列表。这是一个带有静态方法的片段,它给出了 随机 item:
public record item(type type, string name) { public enum type { weapon, armor, helmet, gloves, boots, } private static final random random = new random(); private static final string[] names = { beginner, knight, king, dragon, }; public static item random() { return new item( type.values()[random.nextint(type.values().length)], names[random.nextint(names.length)]); }}
现在,让我们item使用循环创建一个随机列表。代码如下所示:
list items = new arraylist(100);for (int i = 0; i { if (value == null) return 1; return value + 1; }); } return map;}
streams 的代码如下所示:
public map stream(list items) { return items.stream().collect(collectors.tomap( item::type, value -> 1, integer::sum));}
它们看起来截然不同,但它们的表现如何呢?下表是 100 次尝试的平均执行时间:
正如我们在上面的比较表中看到的,streams 和循环在迭代整个列表时显示出很小的执行时间差异。在大多数情况下,这对于其他 stream 方法(如map()、foreach()、reduce()等)是相同的。
并行流优化
因此,我们发现在迭代列表时,流的性能并不比循环更好或更差。然而,streams 有一个循环所不具备的神奇之处:我们可以轻松地利用流进行多线程计算。 所要做的就是使用parallelstream()而不是stream()。
为了了解我们可以从中获得多少影响,让我们看一下下面的示例,其中我们模拟了耗时较长的任务,如下所示:
private void longtask() { // mock long task. try { thread.sleep(1); } catch (interruptedexception e) { throw new runtimeexception(e); }}
循环遍历列表将如下所示:
protected void loop(list items) { for (item item : items) { longtask(); }}
stream将如下所示:
protected void stream(list items) { items.stream().foreach(item -> longtask());}
最后,并行流将如下所示:
protected void parallel(list items) { items.parallelstream().foreach(item -> longtask());}
请注意, onlystream()已更改为parallelstream()。
这是比较:
正如预期的那样,循环和stream几乎没有什么区别。那么并行流呢?耸人听闻!与其他实现相比,它节省了 80% 以上的执行时间!这怎么可能?
对于需要很长时间才能完成并且应该为列表中的每个元素独立完成的任务,它们可以同时运行,我们可以期待显着的改进。这就是并行流正在做的事情。他们将它们分配到多个线程中并使它们同时运行。
并行流并非万能通用,只有当任务是独立的时,它才有用。如果任务不是独立的,并且必须共享相同的资源,则必须使用锁(主要是java中的synchronized关键字)来保证它们的安全,此时它们的运行速度慢于正常的迭代。
3 局限性
然而,stream也有局限性。一种情况是条件循环,另一种情况是重复。让我们看看它们的意思。
条件循环
当我们想要重复直到条件为真但不确定需要多少次迭代时,我们通常使用while循环。
boolean condition = true;while (condition) { ... condition = dosomething();}
使用 streams 表现相同的代码如下所示:
stream.iterate(true, condition -> condition, condition -> dosomething()) .foreach(unused -> ...);
我们可以看到 streams 代码部分会干扰读取,例如condition -> condition检查条件是否为真,unused以及foreach()。考虑到这一点,条件循环最好写在while循环中。
重复
重复是for循环存在的主要原因之一。假设我们想重复这个过程十次。有了for循环,就可以很容易地写成:
for (int i = 0; i ...);
虽然代码可能看起来简洁而正确,但它看起来更侧重于0到10(排除)范围内的值,其中for循环代码可以重复读取十次,因为更常见的做法是这样写repeat:从0开始,以重复次数结束。
4 概括
我们已经对流和循环进行了一些比较。那么……streams 可以取代循环吗?嗯,一如既往,这取决于情况!然而,streams 通常可以为我们提供更简洁、易于阅读的代码和优化。
来源:betterprogramming.pub/can-streams-replace-loops-in-java-f56d4461743a
贸泽电子发布微信支付功能 夺移动支付高地 强化全支付用户体验
2020的网络趋势是怎样的
手机流畅大比拼,一加5夺得榜首!华为P10,小米6不见踪影?
Shark鲨客蒸汽拖把&电动拖把体验,清洁品类的王牌
机器人编程国内外主流赛事及能力测评
为什么需要Streams?它们能取代Java中的for循环吗?
原创教程:怎么选择运放的压摆率?
比亚迪对战吉利 新能源汽车市场再掀风云
变频器的配线注意事项
双向可控硅使用准则_触发电路_工作原理图_双向可控硅测量好坏
基于集成门极换流晶闸管器件实现两电平变频器的设计
我们真的需要区块链来保护隐私吗
“科技创新·打造宝安元宇宙高地”高峰论坛在深成功举办
探秘笔记本手感测试机:品质之道从触感开始
智能家居什么时候才能普及?
锂电池供电 低压输入线性调光方案
华为P10是华为的新一代滑铁卢?华为P10评测:新一代拍照神器
高频雷达与低频率雷达相比有哪些优点
百度数据中心:2012年Q1手机品牌及产品分析
人工智能对英语词汇造成的影响,9个比较有意思的新词汇