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中的各种变量提升(Hoisting)

    首先纠正下,文章标题里的 “变量提升” 名词是随大流叫法,“变量提升” 改为 “标识符提升” 更准确.因为变量一般指使用 var 声明的标识符,JS 里使用 function 声明的标识符也存在提升( ...

  2. Maven下SiteMesh的使用

    先白扯两句,为什么用SiteMesh,当然是减少重复代码,让程序员更加转自己的那一小块. 优点呢:结构化,重用 缺点:呵呵呵,性能,尤其是GC 至于3.0是不是有很大提升,请大神来分析一下. 性能你还 ...

  3. 遍历NSArray, NSDictionary, NSSet的方法总结

    1,for循环读取 NSArray: NSArray *array = /*…*/ ; i<array.count; i++) { id object = array[i]; // do sth ...

  4. Loader加载器

    今天学到了这个Loader,浅谈一下自己的看法: 1.定义 Loader是一个加载器,可以用来它访问数据,可以看做访问数据的机器(好比挖掘机).装再器从android3.0开始引进,它使得在activ ...

  5. iOS tableviewcell 分割线 偏移和颜色

    改变颜色 [_hotProductsTableView setSeparatorColor : kSeparatorColor]; -(void)viewDidLayoutSubviews { [su ...

  6. UIButton、UILabel、UITextField 初学者需要了解的基本定义和常用设置

     以下是三个IOS开发中最常用的控件,作为IOS基础学习教程知识 ,初学者需要了解其基本定义和常用设置,以便在开发在熟练运用. UIButton按钮 第一.UIButton的定义 UIButton * ...

  7. OC中的内存管理

    一. 基本原理 1. 什么是内存管理 移动设备的内存极其有限,每个app所能占用的内存是有限制的 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间.比如回收一些不需 ...

  8. Android 中MyApplication

    package liu.basedemo; import android.app.Activity; import android.app.Application; import java.lang. ...

  9. Tomcat+Nginx+Lvs部署方案与性能调优

    立项要求:用户登录并发数达到1000vuser.原项目设计没有Nginx和LVS,经过性能测试并发数最高到400,就报响应超时,且系统资源消耗严重.经过和架构师商议决定先加入Nginx解决负载均衡处理 ...

  10. Microsoft.Owin.Hosting 实现启动webapp.dll

    Microsoft.Owin.Hosting 下面是 asp.net core 实现 using System;using System.Collections.Generic;using Syste ...