- 我是一个线程(修订版)
- 我是一个 Java class
- Javascript:一个屌丝的逆袭
- Java : 一个帝国的诞生
- JSP 一个装配工的没落
- TCP/IP 之 大明王朝邮差
- TCP/IP 之大明内阁
- TCP/IP 之蓟辽督师
- CPU 阿甘
- CPU 阿甘之烦恼
- CPU 阿甘:函数调用的秘密
- 我是一个网卡
- 我是一个路由器
- 我是一个进程
- 我是一块硬盘(上)
- 我是一块硬盘(下)
- 我是一个键盘
- 张大胖的 socket
- 张大胖学递归
- 学习面向对象的令狐冲
- 张大胖学数据库
- 数据库村的旺财和小强
- 小李的数据库之旅(上)
- 小李的数据库之旅(下)
- 漫画:什么是机器学习?
- 那些烦人的同步和互斥问题
- IE 为什么把火狐和 Chrome 给打伤了?
- 对浏览器村的第二次采访
- 节约标兵 IE 的自述
- EMail 诞生记
- Email 诞生记(下)
- Http 历险记(上)
- Http 历险记(下)-- Struts 的秘密
- 动物王国的面向对象
- 冯·诺伊曼计算机的诞生
- Http Server : 一个差生的逆袭
- 张大胖的加法器
- 从 1 加到 100:一道简单的数学题挑战下你的大脑
- 编程语言
- Javascript:一个屌丝的逆袭
- 计算机语言之战
- 我和编程语言的爱恨情仇(上)
- 我和编程语言的爱恨情仇(下)
- Android 为什么选择了 Java
- iOS 为什么选择了 Object-C?
- Basic : 一个老兵的自述
- Node.js : 我只需要一个店小二
- 命令式编程 vs 声明式编程
- 编译还是解释?
- 程序人生
- “架构师"小赵
- 师兄说
- 师姐说
- 小王的架构师之路
- 小李的版本管理系统
- 小超穿越记
- 小李的 Build 之路(上)
- 小李的 Build 之路(下)
- 张大胖改 Bug
- 我的编程之路--大学趣事
- 码农小王的一天
- 小李在外企
- 张大胖的需求估算
- 从厨师到码农
- 聊一聊那些神一样的程序员们(上)
- 聊一聊那些神一样的程序员们(中)
- 聊一聊那些神一样的程序员们(下)
- 谁是互联网之父?
- 一个价值百万的创业教训
- 让自己与众不同 - 提升工作的价值
- 看看你的“易燃性”
- 从无聊的工作中寻找价值
- 什么样的学生适合报考计算机?
- 谈谈程序员的职业方向(上)
- 谈谈程序员的职业方向(中)
- 谈谈程序员的职业方向(下)
- 谈谈培训班的作用
- 码农需要知道的“潜规则”
- 学习编程的加速度
- 码农在工作中的必备能力
- 码农和英语
- 老司机经验
- 假如时光能够倒流, 我会这么学习 Java
- 假如我是计算机系老师
- 学会编程, 而不是学会 Java
- 从增删改查中突围
- 抽象:程序员必备的能力
- 懒就一个字
- 编程的自学方法
- 小王买房记
- 从一道面试题谈谈一线码农应该具备的基本素质
- 想写框架的看过来
- 苹果手机变砖头以后
- 如何快速的学习一门技术?
- 唯一不变的是变化: 谈谈微信应用号
- 什么是企业应用?
- 勿以浮沙筑高台
- 为什么敏捷开发难于成功?
- localhost vs 127.0.0.1
- GitHub/Stackoverflow 找工作时有什么用?
- 动词 or 名词 :这是一个问题
- 如何选择入行语言
- 有时候,沉默是金
- 零 Bug 的代码是怎么炼成的?
- 浮点数为什么不精确?
- 文章错误大全
- Open Source--不要为了开源而开源
- 一不留神,代码就腐化了
- 先做个“键盘侠”, 再来写程序
- 不加断点调试的程序员是好程序员
- 码农必备技能:烂代码的处理之道(上)
- 码农必备技能:烂代码的处理之道(下)
- 学习数据结构有用吗?
- 从现在开始,丰富你的简历
- 那些永不过时的书,你看过几本吗?
- 学好编程必备的一个品质你知道吗?
- 你最爱的 Java
- 搞懂了这几点,你就学会了 Web 编程
- Spring 的本质系列(1) -- 依赖注入
- Spring 本质系列(2)-AOP
- 三层架构和 MVC 那点事儿
- Java 帝国之拨云见日识回调
- 小张的 Duck Typing
- JDBC 的诞生
- JDBC 后传
- 一个不安分的 JDBC 驱动
- Java 帝国之 Java bean (上)
- Java 帝国之 Java bean(下)
- Java 帝国之函数式编程
- Java 帝国之函数式编程(下)
- 关于 Java 初学者需要知道的 10 件事
- JUnit 你不知道的那些事儿
- 圣诞礼物:Java EE 的历史
- Java EE 读书指南
- 给小白的 Java EE 指南
- 给小白的 Java EE 指南(2)
- 给小白的 Java EE 生存指南(3) : XML
- 给小白的 Java EE 生存指南(4) : 一只叫 Tom 的猫
- 给小白的 Java EE 指南(5) : AJAX
- 给小白的 Java EE 生存指南(6) :Java 反射
- 闲聊
- "饿了么"初体验
- 来自大脑的控诉
- 一个高中生是怎么玩自媒体的?
- 尝试 分答
- 到底应不应该上培训班?
- 自学编程中遇到问题怎么办?
- 据说 99%的初级程序员看完后都不迷茫了
- 一行代码引发的“血案”
- 对一个死锁问题的思考
- 通过外包进入名企
- 请开往十年前的今天
- 为什么自学中最好有个师傅指导一下?
- 这个网站值得你花时间投入
- 为什么你无法坚持自学编程?
Java 帝国之函数式编程(下)
上一篇文章《Java 帝国之函数式编程(上)》说到 Java 帝国为了迎合函数式编程的狂热分子,决定为 Java 语言加上 Labmda 表达式。
同时为了展示“声明式”编程的魅力, 小码哥决定加上一个新的概念: Stream。
等到一切准备停当, 我们召开了一场开发者大会, 决定隆重推出 Java 函数式编程。 1. Stream 小码哥首先展示了 Labmda 表达式 和 类型推断, 大家还比较满意, 台下有人说:
“嗯, 和微软的 C#长的挺像的, 人家用 => ,我们用 -> ”
等到小码哥开始介绍 Stream 的时候, 下面就有人发难了:
“我们不是已经有 IO 流了吗, 字节流,字符流, 怎么又搞个流出来?”
小码哥赶紧解释: “这个 Stream 和 IO 流是不一样的, 这是个新的概念, 主要是为了实现 LISP 中常见的延迟计算(或者惰性求值)的功能”
看到下面的人开始交头接耳, 小码哥说: “大家别着急,先看看这一段代码:”
public class EvenNumber implements Supplier<Long>{ long num = 0; @Override public Long get() { num += 2; return num ; } }
如果持续调用这个类的 get() 方法, 就可以得到所有的偶数, 现在我们就可以用它产生一个流:
Stream<Long> numbers = Stream.generate( new EvenNumber());
这个 Stream 其实就代表了一个无穷无尽的偶数序列, 只是它还没有计算出来而已( 惰性的/延迟的) 如果试图打印每个元素,那就开始计算了, 你就发现它会一直运行下去
numbers.forEach(x->System.out.println(x));
当然,无限的序列是无意义的, 各位在编程中肯定会限定长度的:
numbers.limit(5).forEach(x->System.out.println(x)); 输出: 2 4 6 8 10 2. 延迟计算 现在台下的人安静了, 小码哥有了信心, 开始介绍对 Java 集合框架的改进:
“为了方便大家使用函数式风格,我们对集合框架中的类库做了极大的增强, 每个集合都可以变成一个 stream , 然后就可以使用那些著名的 map , reduce , filter 等函数了”
Arrays.asList("Hello","Java8","Java7"). stream () . map (s -> s.toUpperCase())
map 是个高阶函数, 它接受了一个 Labmda 表达式(匿名函数)作为参数, 把 Stream 中的元素做了变换: 字符串变成了大写 , 然后返回了一个新的 Stream
这也是延迟计算, 即使你加了一个打印语句, 也不会有任何任何输出:
Arrays.asList("Hello","Java8","Java7").stream() .map(s -> { System.out.println(s); return s.toUpperCase(); });
由于 map 返回了一个新的 Stream, 可以在新的 Stream 上继续操作,例如 filter : 把以 J 开头的字符串找出来, filter 的结果仍然是个 Stream.
Arrays.asList("Hello","Java8","Java7").stream() . map (s ->s.toUpperCase()) . filter (s -> s.startsWith("J"));
最后你就可以用 forEach 输出了,和惰性求值相反, forEach 是个立即求值的函数:
Arrays.asList("Hello","Java8","Java7").stream() . map (s ->s.toUpperCase()) . filter (s -> s.startsWith("J")) . forEach (s -> System.out.println(s));
如你所料, 这里的输出是: JAVA 8 JAVA 7
台下有人举手问到: “既然是延迟计算, 那列表中的元素在流中到底是怎么处理的?”
小码哥回答:“这是个好问题, 我们加点打印语句看看:”
Arrays.asList("Hello","Java8","Java7").stream() .map(s -> { System.out.println("map: "+ s); return s.toUpperCase();}) .filter(s -> { System.out.println("filter:"+ s); return s.startsWith("J");}) .forEach(s -> System.out.println(s));
系统的输出是这样的: map: Hello filter:HELLO map: Java8 filter:JAVA8 JAVA8 map: Java7 filter:JAVA7 JAVA7
由此可以看出, 系统先取到初始 Stream 中的第一个元素“Hello” , 做 map 操作,变成 "HELLO", 然后传递给 filter , filter 做了判断, 发现不是以"J" 开头的字符串, 立刻停止, 不会走到 forEach 那里。
接下来取第二个元素"Java8 ", 再经过 map, filter, 这时候发现符合规则, 就走到了 forEach 那里, 打印出来了。
对第三个元素“Java 7” 也是类似处理
还有人问道: “s ->s.toUpperCase() 是不是和 java.util.function.Function 这个接口相匹配?
小码哥说: “是的, 这是 JDK 内置的一个接口, 还有那个 s->s.startsWith("J") 和 java.util.function.Predicate 匹配, 这是为了方便大家编程, 不用自己写函数接口了”
“除了 map, filter, 还有哪些可以用的内置函数?”
小码哥说: “我们内置了很多, 像 reduce , max ,min , collect, flatMap, 大家可以看看我们会议分发的手册, 使用这些函数, 我们就不用考虑集合处理的细节了, 基本上能做到声明式编程了。”
我看出大家有点小失望, 毕竟和纯正的函数式编程相差还比较远, 只能说是给面向对象的 Java 增加了一点函数式的特性。 3. 函数式编程的好处 这时候台下有个热爱 Ruby 的家伙叫了起来: “啊 ! 我知道怎么利用 Java 函数式编程写出 Ruby 风格的代码了,举个例子”
public class Connection { public void open (){ System.out.println("Open connection"); } public void close (){ System.out.println("Close connection"); } public void read (){ System.out.println("Read from connection"); } }
正常的使用是这样的, 调用方很麻烦, 得用 try finally 确保连接关闭。
Connection conn = new Connection(); conn.open(); try{ conn.read(); }finally{ // 一定得确保连接在 finally 中关闭 conn.close(); }
有了函数式编程, 我们可以在 Connection 添加这么一个函数: public class Connection { ......
注意这个方法,它已经把打开连接,关闭连接搞定了
public static void open(
Consumer<Connection> consumer ){
Connection conn = new Connection(); conn.open(); try{ consumer.accept(conn); }finally{ conn.close(); } } }
然后调用方就简单了: Connection.open(conn -> conn.read());
用这种方式, 调用方根本不用处理连接的打开和关闭问题了 ! 只需要关注自己想要调用的逻辑 实在是太爽了! 这和 Ruby 风格非常像 :
Connection.open do |conn| conn.read end
我和小码哥都大喜过望: 没有想到还有这么一个同盟军 !
另外一个家伙说:“这的确是个好例子, 其实大家想想在 java 世界大行其道的设计模式, 很多时候设计模式就是想把一个行为封装起来, 到处传递而已, 只是当时我们没有函数式编程的概念, 只好用个类来封装一个行为,显得很笨拙, 最典型的就是策略模式。”
小码哥回应说: “对的, 大家可以充分的发掘一下函数式编程的威力, 不但可以简化集合框架的操作, 还能简化代码, 简化设计模式, 甚至也能向 Ruby 那样,开发出领域特定语言(DSL) 出来 。 ” 4. 并行 我看发布会临近尾声, 小码哥竟然还没有介绍并行化, 赶紧提醒他: 小码,快讲讲数据并行化啊。 “对了, Java 函数式编程还给大家提供了另外一件福利:并行化“ 小码哥终于要补上这一项了
“把代码变成并行化代码异常简单, 只需要把 stream() 操作改成 parallelStream() 就可以了, 例如下面这个计算素数的程序”
List<Integer> numbers= ..... List<Integer> prims = numbers. parallelStream() .filter(i -> Util.isPrim(i) ) .collect(Collectors.toList())
“再比如对数组的并行排序:Arrays. parallelSort (), 只需要做一点点改动, 剩下的工作就交由 Java 来完成了, 我们会把数据自动进行分块, 分配到各个 CPU 核心上去运行, 最后把结果收集回来, 一个简单的变化就能极大的提升性能。”
我听到台下响起了欢呼声!
但是小码哥接着就泼了一盆冷水: ”使用并行 stream 的时候要注意,它不一定 100%能提高性能,因为这依赖很多因素 ,例如输入的数据是否容易分解成块, 是不是 CPU 密集型的任务, 有没有 IO 等待操作等等...... ”
我就知道技术人员太老实 ,不会忽悠, 眼瞅着热烈的气氛要冷却下来, 我赶紧上台, 抢过话筒说: “这些细节大家下来再和小码哥聊吧, 我们今天的发布会就到这里, 再见。”
Java 的函数式编程就这么发布了, 帝国程序员的工具箱里又多了一件工具, 虽然不是纯正的函数式编程, 但我们确实可以用它来写出更简洁的代码, 希望大家能够喜欢它。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论