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

时间:2024-05-03 12:36:43
  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 -->
Guava 是个风火轮之函数式编程(3)——表处理

用云栖社区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 -->