Guava 是个风火轮之函数式编程(3)——表处理
- 云栖社区>
- 博客列表>
- 正文
Guava 是个风火轮之函数式编程(3)——表处理
潘家邦
2016-01-26 13:19:21 浏览1062
评论0
摘要:
早先学习 Scheme 的时候,就已经对 Lisp 那行云流水般的表处理手段一见倾心。后来使用 Python 做数据处理时,语言内置的高阶函数更是得心应手。工作之后开始使用 Java,一开始的时候仿佛回到了石器时代。
直到后来我找到了 Guava,才终于又可以使用熟悉的方式去操纵集合。
函数式风格...
早先学习 Scheme 的时候,就已经对 Lisp 那行云流水般的表处理手段一见倾心。后来使用 Python 做数据处理时,语言内置的高阶函数更是得心应手。工作之后开始使用 Java,一开始的时候仿佛回到了石器时代。
直到后来我找到了 Guava,才终于又可以使用熟悉的方式去操纵集合。
函数式风格的表处理让开发者从底层的迭代处理中解放出来,从更加抽象的层面来思考问题。然而,Guava 仅仅实现了 map、filter 者两个高阶函数,并没有实现 reduce。
映射
表处理中有这样一个操作,将某个函数分别应用到集合的每个元素上,将返回值集合以列表返回,这个操作一般命名为 map,实现为一个高阶函数。
在 Guava 中,提供同样操作的方法是一个静态函数,Collections2#transform。按照 map 函数的约定俗成,第一个参数是被操作集合,第二个参数是操作函数,返回值是结果集合。也许是出于避免函数名和变量名冲突的考虑,Guava 没有像其他语言那样使用 map 作为函数名,而是使用了 transform。(想想我们写的生产代码里面有多少个哈希表以 map 命名,回去面壁……)
Function<Integer, Integer> square = new Function<Integer, Integer>() {
public Integer apply(Integer input) {
return input * input;
}
};
Collections2.transform(Lists.newArrayList(1, 2, 3), square);//[1, 4, 9]
过滤
高阶函数 filter 的作用和它的名字一样,就是个过滤器,将一个布尔型函数应用到集合的每个元素上,然后根据函数的返回值决定元素是否留在返回值集合中。
在 Guava 中,Collections2#filter 提供了 filter 的功能。这一次 Guava 使用了约定俗成的名字。
Predicate<Integer> isOdd = new Predicate<Integer>() {
public boolean apply(Integer input) {
return (input & 1) != 0;
}
};
Collections2.filter(Lists.newArrayList(1, 2, 3), isOdd);//[1, 3]
折叠
折叠这个操作是把一个列表归并成一个元素,在一些语言中这个操作被称作 fold,另一些称之为 reduce。
在 Python 中,我们假如我们想要实现列表元素的累加,可以写成下面这个样子:
reduce(lambda x, y: x + y, [1,2,3])
在 Clojure 中,我们可以写的更加简单:
(reduce + [1 2 3])
可惜的是,Guava 并没有实现折叠操作。早在 2009 年的时候就有人在 Guava 的 issue1 中提出,为 可迭代的集合增加一个 fold 方法,issue 讨论中大家也是贴出了各自的实现。然而,最后在 15 年 4 月 11 日这个 issue 被关闭了,Guava 的维护者决定不再向 Guava 添加函数式编程的特性,因为 Java 8 出来了。
虽然 Guava 的很多特性都在 Java 8 中得到了实现,但是并不是所有的开发者都能用上 Java 8。对于我们这些不得不使用 Java 7 甚至 Java 6 的开发者来说,Guava 就是帮助我们提升开发效率的神器。
源码分析
Collections2.transform
使用代理模式来实现延迟求值是 Guava 的惯用技法,transform 函数也不例外。
public static <F, T> Collection<T> transform(Collection<F> fromCollection,
Function<? super F, T> function) {
return new TransformedCollection<F, T>(fromCollection, function);
}
TransformedCollection 就是代理类,把传入的被操作集合和操作函数代理了起来,直到必要的时候才调用操作函数获取结果元素。
static class TransformedCollection<F, T> extends AbstractCollection<T> {
final Collection<F> fromCollection;
final Function<? super F, ? extends T> function;
TransformedCollection(Collection<F> fromCollection,
Function<? super F, ? extends T> function) {
this.fromCollection = checkNotNull(fromCollection);
this.function = checkNotNull(function);
}
@Override public void clear() {
fromCollection.clear();
}
@Override public boolean isEmpty() {
return fromCollection.isEmpty();
}
@Override public Iterator<T> iterator() {
return Iterators.transform(fromCollection.iterator(), function);
}
@Override public int size() {
return fromCollection.size();
}
}
因为 Collection 的元素只能通过迭代器去遍历访问,所有我们只需要跟着 iterator 方法走下去,就能搞清楚 transform 的实现。
public static <F, T> Iterator<T> transform(final Iterator<F> fromIterator,
final Function<? super F, ? extends T> function) {
checkNotNull(function);
return new TransformedIterator<F, T>(fromIterator) {
@Override
T transform(F from) {
return function.apply(from);
}
};
}
Iterators.transform 函数返回了一个闭包,继承自抽象类 TransformedIterator。闭包中定义了操作函数的调用时机,那么我们接下来要找的就是 TransformedIterator#transform 的调用者了。
abstract class TransformedIterator<F, T> implements Iterator<T> {
final Iterator<? extends F> backingIterator;
TransformedIterator(Iterator<? extends F> backingIterator) {
this.backingIterator = checkNotNull(backingIterator);
}
abstract T transform(F from);
@Override
public final boolean hasNext() {
return backingIterator.hasNext();
}
@Override
public final T next() {
return transform(backingIterator.next());
}
@Override
public final void remove() {
backingIterator.remove();
}
}
TransformedIterator 这个抽象迭代器在 next 方法中完成了对 transform 的调用。也就是说,操作集合元素的时机被推迟到了遍历时,没有买卖就没有杀害!(什么鬼……)
终于集齐全部碎片,把拼图完成了!Guava 为了实现这个代理模式和延迟求值可谓煞费苦心,嵌套了一层又一层的。可见把代码写到及格也许只要几分钟,写到接近满分可就没那么容易了。
<!-- 登录查看 begin -->
<!-- 登录查看 end -->

用云栖社区APP,舒服~
<a href="#comment" class="y-btn-blue">评论文章 (<i>0</i>)</a>
<span id="vote_btn" has_voted="" data-aid="4044" data-islogin="false" title="点赞" class="icon-award opt-btn ">(<span id="vote_num">0</span>)</span>
<span id="mark_btn" has_marked="" data-aid="4044" data-islogin="false" title="收藏" class="icon-collection opt-btn ">(<span id="mark_num">0</span>)</span>
<dl class="share-to">
<dt>分享到:</dt>
<dd>
<a href="http://service.weibo.com/share/share.php?title=Guava+%E6%98%AF%E4%B8%AA%E9%A3%8E%E7%81%AB%E8%BD%AE%E4%B9%8B%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B%283%29%E2%80%94%E2%80%94%E8%A1%A8%E5%A4%84%E7%90%86+%0A%E6%97%A9%E5%85%88%E5%AD%A6%E4%B9%A0+Scheme+%E7%9A%84%E6%97%B6%E5%80%99%EF%BC%8C%E5%B0%B1%E5%B7%B2%E7%BB%8F%E5%AF%B9+Lisp+%E9%82%A3%E8%A1%8C%E4%BA%91%E6%B5%81%E6%B0%B4%E8%88%AC%E7%9A%84%E8%A1%A8%E5%A4%84%E7%90%86%E6%89%8B%E6%AE%B5%E4%B8%80%E8%A7%81%E5%80%BE%E5%BF%83%E3%80%82%E5%90%8E%E6%9D%A5%E4%BD%BF%E7%94%A8+Python+%E5%81%9A%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86%E6%97%B6%EF%BC%8C%E8%AF%AD%E8%A8%80%E5%86%85%E7%BD%AE%E7%9A%84%E9%AB%98%E9%98%B6%E5%87%BD%E6%95%B0%E6%9B%B4%E6%98%AF%E5%BE%97%E5%BF%83%E5%BA%94%E6%89%8B%E3%80%82%E5%B7%A5%E4%BD%9C%E4%B9%8B%E5%90%8E%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8+Java%EF%BC%8C%E4%B8%80%E5%BC%80%E5%A7%8B%E7%9A%84%E6%97%B6%E5%80%99%E4%BB%BF%E4%BD%9B%E5%9B%9E%E5%88%B0%E4%BA%86%E7%9F%B3%E5%99%A8%E6%97%B6%E4%BB%A3%E3%80%82%0A%E7%9B%B4%E5%88%B0%E5%90%8E%E6%9D%A5%E6%88%91%E6%89%BE%E5%88%B0%E4%BA%86+Guava%EF%BC%8C%E6%89%8D%E7%BB%88%E4%BA%8E%E5%8F%88%E5%8F%AF%E4%BB%A5%E4%BD%BF%E7%94%A8%E7%86%9F%E6%82%89%E7%9A%84%E6%96%B9%E5%BC%8F%E5%8E%BB%E6%93%8D%E7%BA%B5%E9%9B%86%E5%90%88%E3%80%82%0A%E5%87%BD%E6%95%B0%E5%BC%8F%E9%A3%8E%E6%A0%BC...&url=https%3A%2F%2Fyq.aliyun.com%2Farticles%2F4044" target="_blank" class="weibo icon-weibo-o"></a>
<div class="wechat">
<i class="icon-wechat"></i>
<img src="/api/qrcode?size=140&key=70d4b307e4b1e47892d2870165b1a70f7f3728d4&text=https%3A%2F%2Fyq.aliyun.com%2Farticles%2F4044" alt="">
</div>
</dd>
</dl>
网友评论
<form accept-charset="UTF-8" action="/comments" method="POST" data-remote="true" data-target="#comments" class="js-comment-create js-active-on-valid">
<input type="hidden" name="type" value="article">
<input type="hidden" name="yunqi_csrf" value="0BOZZZMILS">
<input type="hidden" name="isCheck" value="1">
<input type="hidden" name="pid" value="4044">
<div class="form-group">
<div class="editor">
<div class="login_tips" id="comment">登录后可评论,请 <a id="fast_login" href="https://account.aliyun.com/login/login.htm?from_type=yqclub&oauth_callback=https%3A%2F%2Fyq.aliyun.com%2Farticles%2F4044%3Fdo%3Dlogin" class="s4" hidefocus="true" rel="nofollow">登录</a> 或 <a href="https://account.aliyun.com/register/register.htm?from_type=yqclub&oauth_callback=https%3A%2F%2Fyq.aliyun.com%2Farticles%2F4044%3Fdo%3Dlogin" target="_blank" class="s4" hidefocus="true" rel="nofollow">注册</a></div>
</div><!-- /.editor -->
</div>
<div class="form-group text-right">
<a href="#modal-login" class="btn btn-default" data-toggle="modal">评论</a>
</div>
</form>
</section><!-- /.comments -->
Guava 是个风火轮之函数式编程(3)——表处理的更多相关文章
- guava函数式编程
[Google Guava] 4-函数式编程 原文链接 译文链接 译者:沈义扬,校对:丁一 注意事项 截至JDK7,Java中也只能通过笨拙冗长的匿名类来达到近似函数式编程的效果.预计JDK8中会有所 ...
- Java经典类库-Guava中的函数式编程讲解
如果我要新建一个java的项目,那么有两个类库是必备的,一个是junit,另一个是Guava.选择junit,因为我喜欢TDD,喜欢自动化测试.而是用Guava,是因为我喜欢简洁的API.Guava提 ...
- guava function and predicate 函数式编程
@Test public void function(){ List<String> list = Lists.newArrayList("1","2&quo ...
- 使用Guava进行函数式编程
本文翻译自Getting Started with Google Guava这本书,如有翻译不足的地方请指出. 在这一章,我们开始注意到使用Guava进行编写代码会更加简单.我们将看看如何使用Guav ...
- Guava 教程(3):Java 的函数式编程,通过 Google Collections 过滤和调用
原文出处: oschina 在本系列博客的第一.二部分,我介绍了非常优秀的Google Collections和Guava包.本篇博客中我们来看看如何使用Google Collections来做到过滤 ...
- JDK 8 函数式编程入门
目录 1. 概述 1.1 函数式编程简介 1.2 Lambda 表达式简介 2. Lambda 表达式 2.1 Lambda 表达式的形式 2.2 闭包 2.3 函数接口 3. 集合处理 3.1 St ...
- java8函数式编程(转载)
1. 概述 1.1 函数式编程简介 我们最常用的面向对象编程(Java)属于命令式编程(Imperative Programming)这种编程范式.常见的编程范式还有逻辑式编程(Logic Progr ...
- angular2系列教程(六)两种pipe:函数式编程与面向对象编程
今天,我们要讲的是angualr2的pipe这个知识点. 例子
- [学习笔记]JavaScript之函数式编程
欢迎指导与讨论:) 前言 函数式编程能使我们的代码结构变得简洁,让代码更接近于自然语言,易于理解. 一.减少不必要的函数嵌套代码 (1)当存在函数嵌套时,若内层函数的参数与外层函数的参数一致时,可以这 ...
随机推荐
- 传说中的AutoCAD公司 - 欧特克(Autodesk)招聘开发顾问-上海或北京
如果您热衷新技术,垂涎科技前沿,对编程有狂热的热情,乐于帮助别人打造解决方案,喜爱分享和交流,英文沟通无障碍,来吧,把简历丢过来! 如果您刚毕业不久,那也不要因为工作经历尚浅而怯步,我们也非常欢迎您! ...
- JDBC连接MySQL 方法 实例及资料收集
JDBC连接MySQL 方法 实例及资料收集 准备工作 首先,安装MySQL,配置用户名和密码,创建数据库. 可参见之前的文章: http://www.cnblogs.com/mengdd/p/315 ...
- 开发者账号续期后,itunes停止付款了
开发者账号过期后,没有及时续期,等再续期后,itunes停止付款到公司银行账户了.过了一个多月了还是没有收到itunes的付款.然后开始联系苹果客服和技术支持,他们都说只能通过itunes的“联系我们 ...
- java field, property,variable及getField和getDeclaredField的区别
java 里面的field ,property, attribute,variable的区别 field: 就是定义的用于保存数据的字段 property: property是用于描述类中的特征,所以 ...
- 你真的了解UIWindow吗?
一:首先查看一下关于UIWindow的定义 NS_CLASS_AVAILABLE_IOS(2_0) @interface UIWindow : UIView //window的屏幕,默认是 [UISc ...
- 在iOS开发过程中你遇到这个问题了么?
1.问题:加载UIWebView底部有黑色边框问题. 设置UIWebView opaque为NO,然后设置其背景色为clearColor. 2.问题:iPhone真机输出[UIScreen mainS ...
- 【Android】友盟的自动更新组件
前言 又好又专业的服务能帮开发者省很多时间.一开始做项目也准备自己来统计数据.自己做自动更新,随着使用友盟服务的时间增加,渐渐放弃了这种想法,转而研究如何更充分的使用,这里分享一下使用自动更新组件的心 ...
- IOS开发之功能模块--给任意的UIView添加点击事件
前言:好久没写博客,今天来一波.我在实际项目开发中,会遇到这样功能需求:我已经搭好了iOS的UI界面,但是很多界面的子View用了UIView,然后这些UIView中用了UILabel和UIImage ...
- MojoDatabase 源码学习之对象映射
Mojo-database是我个人比较喜欢多开源项目,下文是该项目打介绍和地址: mojo-database 简介: MojoDatabase is an ActiveRecord-like ORM ...
- art.dialog 返回提示
<form target="_top" /> 1 如果加 target="_top" 提示跳出子页面 2 如果不加则在子页面提示