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. Javascript到PHP加密通讯的简单实现

    其实内容主要来源于上一篇博文,只是重新组织了语言,并做了原理性的阐述,更容易理解:P ----------------------------------------- 华丽的分割线 -------- ...

  2. 为 MDS 修改 SharePoint 2013组件

    了解如何修改 SharePoint 项目中的组件以在 SharePoint 2013 中利用最少下载策略(MDS).   本文内容 为何修改 SharePoint 组件? 母版页 ASP.NET 页面 ...

  3. 如何:对 SharePoint 列表项隐藏 ECB 中的菜单项

    可以通过使用功能框架向编辑控制块 (ECB) 菜单添加新的自定义操作.但是,您不能使用此方法进行相反的操作,即隐藏现有的 ECB 菜单项,因为它们是通过使用 ECMAScript(JavaScript ...

  4. 如何为基于windows验证的站点的某个页面、文件或文件夹单独设置匿名访问

    在MOSS的项目中,我们经常碰到要单独为基于windows验证的站点的某个页面.文件或文件夹单独设置匿名访问即不登录就可以直接访问.比如说站点的A的某些图片或文件URL存在B站点下的文件夹下.此时访问 ...

  5. iOS中View生命周期与布局

    生命周期 说到view的生命周期一般都是指视图控制器的view生命周期. view的创建:loadView 视图控制器(UIViewController)及其子类,无论是手写代码还是storyboar ...

  6. 委托 C#

    这些就是自己的理解. 委托的用处就是把方法传递给其他方法. 1委托的使用更类差不多,也 是需要先定义再实例化. 2Action<T>和Func<T>委托 3多播委托 4.匿名方 ...

  7. 世道变了 – 你愿意成为微软认证Linux工程师吗?

     随笔世道变了 – 你愿意成为微软认证Linux工程师吗? 世道变了 – 你愿意成为微软认证Linux工程师吗?  leixu十二月 14, 2015随笔 2015年12月9日,微软发布了全新的MCS ...

  8. ios界面布局整理

    1 UIView 1.1 创建自定义的UIView的xib文件 [1]设置 file's Owner的 Custom Class 中的class 执行自定义控件类 [2]设置当前UIView 的屏幕大 ...

  9. 解决easy ui 1.4datebox控件不能清空的问题

    用easy ui遇到这个问题,在网上找到了解决方案,不过是1.3.6版本的.现提供1.4版本的修改的具体位置和代码. 我们用的是这个 修改位置:12739行,添加代码: , { text: funct ...

  10. JavaScript(五)——插入地图

    代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3. ...