Effective Java~45. 谨慎使用Stream

在 Java 8 中添加了 Stream API,以简化顺序或并行执行批量操作的任务。 该 API 提供了两个关键的抽象:流(Stream),表示有限或无限的数据元素序列,以及流管道 (stream pipeline),表示对这些元素的多级计算。 Stream 中的元素可以来自任何地方。 常见的源包括集合,数组,文件,正则表达式模式匹配器,伪随机数生成器和其他流。流中的数据元素可以是对象引用或基本类型。 支持三种基本类型:int,long 和 double。

流管道由源流(source stream)的零或多个中间操作和一个终结操作组成。每个中间操作都以某种方式转换流,例如将每个元素映射到该元素的函数或过滤掉所有不满足某些条件的元素。中间操作都将一个流转换为另一个流,其元素类型可能与输入流相同或不同。终结操作对流执行最后一次中间操作产生的最终计算,例如将其元素存储到集合中、返回某个元素或打印其所有元素。

管道延迟(lazily)计算求值:计算直到终结操作被调用后才开始,而为了完成终结操作而不需要的数据元素永远不会被计算出来。 这种延迟计算求值的方式使得可以使用无限流。 请注意,没有终结操作的流管道是静默无操作的,所以不要忘记包含一个。

        Stream API 流式的(fluent)::它设计允许所有组成管道的调用被链接到一个表达式中。事实上,多个管道可以链接在一起形成一个表达式。

        默认情况下,流管道按顺序 (sequentially) 运行。 使管道并行执行就像在管道中的任何流上调用并行方法一样简单,但很少这样做(第 48 个条目)。

        Stream API 具有足够的通用性,实际上任何计算都可以使用 Stream 执行,但仅仅因为可以,并不意味着应该这样做。如果使用得当,流可以使程序更短更清晰;如果使用不当,它们会使程序难以阅读和维护。对于何时使用流没有硬性的规则,但是有一些启发。

        考虑以下程序,该程序从字典文件中读取单词并打印其大小符合用户指定的最小值的所有变位词(anagram)组。如果两个单词由长度相通,不同顺序的相同字母组成,则它们是变位词。程序从用户指定的字典文件中读取每个单词并将单词放入 map 对象中。map 对象的键是按照字母排序的单词,因此『staple』的键是『aelpst』,『petals』的键也是『aelpst』:这两个单词就是同位词,所有的同位词共享相同的依字母顺序排列的形式(或称之为alphagram)。map 对象的值是包含共享字母顺序形式的所有单词的列表。 处理完字典文件后,每个列表都是一个完整的同位词组。然后程序遍历 map 对象的 values() 的视图并打印每个大小符合阈值的列表:

// Prints all large anagram groups in a dictionary iteratively
public class Anagrams {public static void main(String[] args) throws IOException {File dictionary = new File(args[0]);int minGroupSize = Integer.parseInt(args[1]);Map<String, Set<String>> groups = new HashMap<>();try (Scanner s = new Scanner(dictionary)) {while (s.hasNext()) {String word = s.next();groups.computeIfAbsent(alphabetize(word), (unused) -> new TreeSet<>()).add(word);}}for (Set<String> group : groups.values())if (group.size() >= minGroupSize)System.out.println(group.size() + ": " + group);}private static String alphabetize(String s) {char[] a = s.toCharArray();Arrays.sort(a);return new String(a);}
}

        现在考虑以下程序,它解决了同样的问题,但大量过度使用了流。 请注意,整个程序(打开字典文件的代码除外)包含在单个表达式中。 在单独的表达式中打开字典文件的唯一原因是允许使用 try-with-resources 语句,该语句确保关闭字典文件:

// Overuse of streams - don't do this!
public class Anagrams {public static void main(String[] args) throws IOException {Path dictionary = Paths.get(args[0]);int minGroupSize = Integer.parseInt(args[1]);try (Stream<String> words = Files.lines(dictionary)) {words.collect(groupingBy(word -> word.chars().sorted().collect(StringBuilder::new,(sb, c) -> sb.append((char) c),StringBuilder::append).toString())).values().stream().filter(group -> group.size() >= minGroupSize).map(group -> group.size() + ": " + group).forEach(System.out::println);}}
}

        如果你发现这段代码难以阅读,不要担心;你不是一个人。它更短,但是可读性也更差,尤其是对于那些不擅长使用流的程序员来说。过度使用流使程序难于阅读和维护

        幸运的是,有一个折中的办法。下面的程序解决了同样的问题,使用流而不过度使用它们。其结果是一个比原来更短更清晰的程序:

// Tasteful use of streams enhances clarity and conciseness
public class Anagrams {public static void main(String[] args) throws IOException {Path dictionary = Paths.get(args[0]);int minGroupSize = Integer.parseInt(args[1]);try (Stream<String> words = Files.lines(dictionary)) {words.collect(groupingBy(word -> alphabetize(word))).values().stream().filter(group -> group.size() >= minGroupSize).forEach(g -> System.out.println(g.size() + ": " + g));}}// alphabetize method is the same as in original version
}

        请注意,仔细选择 lambda 参数名称。 上面程序中参数 g 应该真正命名为 group,但是生成的代码行对于本书来说太宽了。 在没有显式类型的情况下,仔细命名 lambda 参数对于流管道的可读性至关重要。

另请注意,单词字母化是在单独的 alphabetize 方法中完成的。 这通过提供操作名称并将实现细节保留在主程序之外来增强可读性。 使用辅助方法对于流管道中的可读性比在迭代代码中更为重要,因为管道缺少显式类型信息和命名临时变量

流可以很容易地做一些事情:

· 统一转换元素序列
· 过滤元素序列
· 使用单个操作组合元素序列 (例如添加、连接或计算最小值)
· 将元素序列累积到一个集合中,可能通过一些公共属性将它们分组
· 在元素序列中搜索满足某些条件的元素

        总之,有些任务最好使用流来完成,有些任务最好使用迭代来完成。将这两种方法结合起来,可以最好地完成许多任务。对于选择使用哪种方法进行任务,没有硬性规定,但是有一些有用的启发式方法。在许多情况下,使用哪种方法将是清楚的;在某些情况下,则不会很清楚。如果不确定一个任务是通过流还是迭代更好地完成,那么尝试这两种方法,看看哪一种效果更好

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

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

相关文章

shell 中 $(( )) 与 $( ) 还有 ${ } 的区别

From&#xff1a;http://blog.chinaunix.net/uid-14351756-id-2820651.html $( ) 与 (反引号) 在 bash shell 中&#xff0c;$( ) 与 (反引号) 都是用来做命令替换用(command substitution)的。 所谓的命令替换与我们第五章学过的变量替换差不多&#xff0c;都是用来重组命…

最近发包给朋友,搞定软件小活儿、解决小功能模块的感受

有时候也想&#xff0c;所有的事情都靠自己解决&#xff0c;太辛苦了太累了&#xff0c;在不差钱的这个年代&#xff0c;有些能让别人做的事情&#xff0c;就让别人做吧&#xff0c;但是一直找不到合适的人&#xff0c;什么叫合适的人&#xff1f;我简单的说几下&#xff0c;不…

c语言数列偶数在奇数前面,奇数

奇偶校验2021-05-19 15:03:06奇偶校验(Parity Check)是一种校验代码传输正确性的方法。根据被传输的一组二进制代码的数位中“1”的个数是奇数或偶数来进行校验。采用奇数的称为奇校验&#xff0c;反之&#xff0c;称为偶校验。奇偶校验需要一位校验位&#xff0c;即使用串口通…

利用自定义web-font实现数据防采集

From&#xff1a;https://blog.csdn.net/fdipzone/article/details/68166388 完整源码&#xff1a;https://download.csdn.net/download/fdipzone/9798142 web-font介绍 web-font 是 CSS3 中的一种标记 font-face&#xff0c;在 font-face 声明里&#xff0c;你可以声明一种字…

数字证书~证书链

来源&#xff1a;客户端认证https服务端证书过程详解——证书链_huzhenv5的博客-CSDN博客_证书链认证过程 基本概念 证书 首先&#xff0c;我们看看在wikipedia上对证书的定义&#xff0c;In cryptography, a public key certificate (also known as a digital certificate o…

修改Netbeas的注释结构

Eclipse修改注释的方法&#xff1a;  窗口-》首选项-》java-》代码样式-》代码模板-》注释-》方法-》确定&#xff0c;下面就可以修改了  Netbeans修改注释的方法&#xff1a;  工具->模板 打开 看见那一排列表没有 展开Java 选择Java类 点击 在编辑器中打开 …

c语言assign用法,object-c语言的nonatomic,assign,copy,retain的区别

nonatomic&#xff1a;非原子性访问&#xff0c;不加同步&#xff0c;多线程并发访问会提高性能。如果不加此属性&#xff0c;则默认是两个访问方法都为原子型事务访问。(atomic是Objc使用的一种线程保护技术&#xff0c;基本上来讲&#xff0c;是防止在写未完成的时候被另外一…

反反爬技术,破解猫眼网加密数字

From&#xff1a;https://blog.csdn.net/qq_31032181/article/details/79153578 From&#xff1a;http://www.freebuf.com/news/140965.html 利用自定义web-font实现数据防采集&#xff1a;http://blog.csdn.net/fdipzone/article/details/68166388 利用前端字体文件(.ttf)混…

数字签名,数字证书,证书链原理

来源&#xff1a;数字签名&#xff0c;数字证书&#xff0c;证书链原理&#xff08;图文详解&#xff09;_Ruby丶彬的博客-CSDN博客_证书链验证原理 数字签名&#xff0c;数字证书&#xff0c;加密简述 数字签名&#xff1a;谈及数字签名&#xff0c;就如小时候老师叫把卷子或…

成都信息工程c语言题库,成都信息工程学院C语言考试题及答案

}in 0;while (sum < im) { }sum - in; in--;printf(\sum%d\\n\in , sum); return 0;in; sum in;编写一程序P215.C实现以下功能求S1/1&#xff01;1/2&#xff01;1/3&#xff01;…1/N&#xff01;并输出结果(显示时小数部分占16位&#xff0c;计算时要求从第1项开始往后累…

转--javascript 数组

较为上篇更为深刻 原文&#xff1a;http://blog.csdn.net/jaylongli/archive/2009/03/20/4007823.aspx 数组有四种定义的方式 使用构造函数&#xff1a; vara newArray(); varb newArray(8); varc newArray("first", "second", "third"); 或者数…

猫眼 — 破解数字反爬获取实时票房

From&#xff1a;https://zhuanlan.zhihu.com/p/33112359 js分析 猫_眼_电_影 字体文件 font-face&#xff1a;https://www.cnblogs.com/my8100/p/js_maoyandianying.html 解析某电影和某招聘网站的web-font自定义字体&#xff1a;https://www.jianshu.com/p/5400bbc8b634 Fo…

c语言中单精度的有效位数是,C语言中的单精度双精度数的有效位数

#include "stdio.h"#include "conio.h"void main(){printf("%.20f\n",0.1234567890123456789f);getch();}观察在VC6&#xff0c;WinTC中的运行结果&#xff0c;可能是0.1234567891043281560000.12345678901234567700而一般在C语言教材上说&#…

Yoshua Bengio团队通过在网络「隐藏空间」中使用降噪器以提高深度神经网络的「鲁棒性」

原文来源&#xff1a;arXiv 作者&#xff1a;Alex Lamb、Jonathan Binas、Anirudh Goyal、Dmitriy Serdyuk、Sandeep Subramanian、Ioannis Mitliagkas、Yoshua Bengio「雷克世界」编译&#xff1a;嗯~是阿童木呀、KABUDA、EVA导语&#xff1a;深度神经网络在各种各样的重要任…

PDF签名系列(1):PDF签名机制的漏洞分析

来源&#xff1a;PDF签名系列(1):PDF签名机制的漏洞分析 - 知乎 研究PDF文件的签名机制有一段时间了&#xff0c;刚开始学习的时候就看到有提到说&#xff0c;被签名的PDF内容的Range gap&#xff0c;会成为这个机制的漏洞&#xff0c;但是一直不能完全参透。直到昨天看到一篇…

linux date命令显示毫秒,解决MacOS系统中date命令没有毫秒和sha512sum、sha256sum、md5sum等命令的问题...

安装brew/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"使用brew安装coreutilsbrew install coreutils设置系统新的命令软连接sudo ln -sv /usr/local/bin/gsha256sum /usr/local/bin/sha256sumsudo ln -sv /…

PDF签名系列(2):PDF的签名值到底存在哪里?

来源&#xff1a;PDF签名系列(2):PDF的签名值到底存在哪里? - 知乎 研究过PDF签名的同学应该见过下面这张图, 来自ADOBE的文档Acrobat_DigitalSignatures_in_PDF.pdf PDF被签名的内容是整个文档除去signature dictionary里/Contents下面的内容. 签过名的哈希值再连同其他一些…

android zip文件读写,如何直接从.zip文件中读取文件而不在android中提取它

我过去几个月一直在研究android,现在我的问题是读取放在SD卡上的.zip文件.我已经成功完成了在SD卡上下载.zip文件的编码.我有img.zip文件下载到SD卡上.这个img.zip包含5个图像文件.现在我可以直接阅读其内容而不是解压缩img.zip …… ???如果是的话请帮忙.我在互联网上看到了…

jQuery的radio,checkbox,select操作

获取一组radio被选中项的值 var item $(input[nameitems][checked]).val(); 获取select被选中项的文本 var item $("select[nameitems] option[selected]").text(); select下拉框的第二个元素为当前选中值 $(#select_id)[0].selectedIndex 1; radio单选组的第二个…

数字签名时间戳服务器的原理

时间戳服务工作流程&#xff1a; ◆ 用户对文件数据进行Hash摘要处理&#xff1b; ◆ 用户提出时间戳的请求&#xff0c;Hash值被传递给时间戳服务器&#xff1b; ◆ 时间戳服务器对哈希值和一个日期/时间记录进行签名&#xff0c;生成时间戳&#xff1b; ◆ 时间戳数据和文…