1. 云栖社区>
  2. 博客列表>
  3. 正文

Guava 是个风火轮之函数式编程(3)——表处理

潘家邦
2016-01-26 13:19:21 浏览1062
评论0

java
Guava



摘要:

早先学习 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,舒服~

【云栖快讯】有奖热议中:盘点我们的2016!2016年在一行行代码中闪过。这一年你都有哪些技术收获?是博客阅读量又创新高,还是辛苦开发的应用上线。又有什么样的大事件让你记忆犹新呢?一起来聊聊各自的猿路历程吧。  详情请点击
<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...&amp;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&amp;key=70d4b307e4b1e47892d2870165b1a70f7f3728d4&amp;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&amp;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&amp;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)——表处理的更多相关文章

  1. guava函数式编程

    [Google Guava] 4-函数式编程 原文链接 译文链接 译者:沈义扬,校对:丁一 注意事项 截至JDK7,Java中也只能通过笨拙冗长的匿名类来达到近似函数式编程的效果.预计JDK8中会有所 ...

  2. Java经典类库-Guava中的函数式编程讲解

    如果我要新建一个java的项目,那么有两个类库是必备的,一个是junit,另一个是Guava.选择junit,因为我喜欢TDD,喜欢自动化测试.而是用Guava,是因为我喜欢简洁的API.Guava提 ...

  3. guava function and predicate 函数式编程

    @Test public void function(){ List<String> list = Lists.newArrayList("1","2&quo ...

  4. 使用Guava进行函数式编程

    本文翻译自Getting Started with Google Guava这本书,如有翻译不足的地方请指出. 在这一章,我们开始注意到使用Guava进行编写代码会更加简单.我们将看看如何使用Guav ...

  5. Guava 教程(3):Java 的函数式编程,通过 Google Collections 过滤和调用

    原文出处: oschina 在本系列博客的第一.二部分,我介绍了非常优秀的Google Collections和Guava包.本篇博客中我们来看看如何使用Google Collections来做到过滤 ...

  6. JDK 8 函数式编程入门

    目录 1. 概述 1.1 函数式编程简介 1.2 Lambda 表达式简介 2. Lambda 表达式 2.1 Lambda 表达式的形式 2.2 闭包 2.3 函数接口 3. 集合处理 3.1 St ...

  7. java8函数式编程(转载)

    1. 概述 1.1 函数式编程简介 我们最常用的面向对象编程(Java)属于命令式编程(Imperative Programming)这种编程范式.常见的编程范式还有逻辑式编程(Logic Progr ...

  8. angular2系列教程(六)两种pipe:函数式编程与面向对象编程

    今天,我们要讲的是angualr2的pipe这个知识点. 例子

  9. [学习笔记]JavaScript之函数式编程

    欢迎指导与讨论:) 前言 函数式编程能使我们的代码结构变得简洁,让代码更接近于自然语言,易于理解. 一.减少不必要的函数嵌套代码 (1)当存在函数嵌套时,若内层函数的参数与外层函数的参数一致时,可以这 ...

随机推荐

  1. vbs操作excel

    航天金税系统升级,导出的Excel文件格式与原来有的差异,老的数据导入程序识别不了该文件,对比了新老文件后,发现新文件在专票和普票的“份数”行前增加了一行,同时增加了“单据号”列,通过脚本 把这些删除 ...

  2. JavaScript学习07 内置对象

    JavaScript内置对象 图像对象 导航对象 窗口对象 屏幕对象 事件对象 历史对象 文件对象(重要) 锚点对象 链接对象 框架对象 表单对象(重要) 位置对象 JS Window 窗口对象:ht ...

  3. CoreAnimation方法汇总

    使用CoreAnimation一般分为三个部分:1.创建执行动画的CALayer 2.创建动画 3.CALayer 添加Animation CoreAnimation是以锚点为基础. CoreAnim ...

  4. JSONKit does not support Objective-C Automatic Reference Counting(ARC) / ARC forbids Objective-C objects in struct

    当我们在使用JSONKit处理数据时,直接将文件拉进项目往往会报这两个错“JSONKit   does not support Objective-C Automatic Reference Coun ...

  5. WPF之Binding深入探讨

    原文:http://blog.csdn.net/fwj380891124/article/details/8107646 1,Data Binding在WPF中的地位 程序的本质是数据+算法.数据会在 ...

  6. 关于input全选反选恶心的异常情况

    上一篇讲到:第一次点击全选按钮input显示对勾,第二次则不显示,需要用prop来添加checked属性. 但是用prop会出现一个问题,对勾显示,而checked属性不会被添加(比如:$(" ...

  7. 网络编程2--毕向东java基础教程视频学习笔记

    Day 23 08 Udp接收端09 Udp键盘录入数据方式10 Udp聊天11 TCP传输12 TCP传输213 TCP练习14 TCP复制文件 08 Udp接收端 需求:定义一个应用程序,用于接收 ...

  8. iOS开发进阶

    <iOS开发进阶>基本信息作者: 唐巧 出版社:电子工业出版社ISBN:9787121247453上架时间:2014-12-26出版日期:2015 年1月开本:16开页码:268版次:1- ...

  9. knockoutjs with绑定导致unobtrusive validation失效的问题

    如果最初的时候with绑定的对象是空的,那么with绑定内部的unobtrusive validation规则在提交的时候无法生效,无法进行验证. 解决办法: 在提交的时候(或者with绑定的对象非空 ...

  10. 中控考勤仪IFace302多线程操作时无法订阅事件

    场景: 在各办事点安装中控考勤仪Iface302,各办事点的工作人员上下班报到时使用指纹或面纹进行自动登记,验证成功后将与服务吕进行通讯记录相关的考勤信息. 条件限制: 由于Iface302设备不支持 ...