[Google Guava] 4-函数式编程

原文链接 译文链接 译者:沈义扬,校对:丁一

注意事项

截至JDK7,Java中也只能通过笨拙冗长的匿名类来达到近似函数式编程的效果。预计JDK8中会有所改变,但Guava现在就想给JDK5以上用户提供这类支持。

过度使用Guava函数式编程会导致冗长、混乱、可读性差而且低效的代码。这是迄今为止最容易(也是最经常)被滥用的部分,如果你想通过函数式风格达成一行代码,致使这行代码长到荒唐,Guava团队会泪流满面。

比较如下代码:

01 Function<String, Integer> lengthFunction = new Function<String, Integer>() {
02     public Integer apply(String string) {
03         return string.length();
04     }
05 };
06 Predicate<String> allCaps = new Predicate<String>() {
07     public boolean apply(String string) {
08         return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string);
09     }
10 };
11 Multiset<Integer> lengths = HashMultiset.create(
12      Iterables.transform(Iterables.filter(strings, allCaps), lengthFunction));

或FluentIterable的版本

01 Multiset<Integer> lengths = HashMultiset.create(
02     FluentIterable.from(strings)
03         .filter(new Predicate<String>() {
04             public boolean apply(String string) {
05                 return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string);
06             }
07         })
08         .transform(new Function<String, Integer>() {
09             public Integer apply(String string) {
10                 return string.length();
11             }
12         }));

还有

1 Multiset<Integer> lengths = HashMultiset.create();
2 for (String string : strings) {
3     if (CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) {
4         lengths.add(string.length());
5     }
6 }

即使用了静态导入,甚至把Function和Predicate的声明放到别的文件,第一种代码实现仍然不简洁,可读性差并且效率较低。

截至JDK7,命令式代码仍应是默认和第一选择。不应该随便使用函数式风格,除非你绝对确定以下两点之一:

  • 使用函数式风格以后,整个工程的代码行会净减少。在上面的例子中,函数式版本用了11行, 命令式代码用了6行,把函数的定义放到另一个文件或常量中,并不能帮助减少总代码行。
  • 为了提高效率,转换集合的结果需要懒视图,而不是明确计算过的集合。此外,确保你已经阅读和重读了Effective Java的第55条,并且除了阅读本章后面的说明,你还真正做了性能测试并且有测试数据来证明函数式版本更快。

请务必确保,当使用Guava函数式的时候,用传统的命令式做同样的事情不会更具可读性。尝试把代码写下来,看看它是不是真的那么糟糕?会不会比你想尝试的极其笨拙的函数式 更具可读性。

Functions[函数]和Predicates[断言]

本节只讨论直接与Function和Predicate打交道的Guava功能。一些其他工具类也和”函数式风格”相关,例如Iterables.concat(Iterable<Iterable>),和其他用常量时间返回视图的方法。尝试看看2.3节的集合工具类

Guava提供两个基本的函数式接口:

  • Function<A, B>,它声明了单个方法B apply(A input)。Function对象通常被预期为引用透明的——没有副作用——并且引用透明性中的”相等”语义与equals一致,如a.equals(b)意味着function.apply(a).equals(function.apply(b))。
  • Predicate<T>,它声明了单个方法boolean apply(T input)。Predicate对象通常也被预期为无副作用函数,并且”相等”语义与equals一致。

特殊的断言

字符类型有自己特定版本的Predicate——CharMatcher,它通常更高效,并且在某些需求方面更有用。CharMatcher实现了Predicate<Character>,可以当作Predicate一样使用,要把Predicate转成CharMatcher,可以使用CharMatcher.forPredicate。更多细节请参考第6章-字符串处理。

此外,对可比较类型和基于比较逻辑的Predicate,Range类可以满足大多数需求——它表示一个不可变区间。Range类实现了Predicate,用以判断值是否在区间内。例如,Range.atMost(2)就是个完全合法的Predicate<Integer>。更多使用Range的细节请参照第8章。

操作Functions和Predicates

Functions提供简便的Function构造和操作方法,包括:

forMap(Map<A, B>) compose(Function<B, C>, Function<A, B>) constant(T)
identity() toStringFunction()

细节请参考Javadoc。

相应地,Predicates提供了更多构造和处理Predicate的方法,下面是一些例子:

instanceOf(Class) assignableFrom(Class) contains(Pattern)
in(Collection) isNull() alwaysFalse()
alwaysTrue() equalTo(Object) compose(Predicate, Function)
and(Predicate...) or(Predicate...) not(Predicate)

细节请参考Javadoc。

使用函数式编程

Guava提供了很多工具方法,以便用Function或Predicate操作集合。这些方法通常可以在集合工具类找到,如Iterables,Lists,Sets,Maps,Multimaps等。

断言

断言的最基本应用就是过滤集合。所有Guava过滤方法都返回”视图”——译者注:即并非用一个新的集合表示过滤,而只是基于原集合的视图

集合类型 过滤方法
Iterable Iterables.filter(Iterable, Predicate)FluentIterable.filter(Predicate)
Iterator Iterators.filter(Iterator, Predicate)
Collection Collections2.filter(Collection, Predicate)
Set Sets.filter(Set, Predicate)
SortedSet Sets.filter(SortedSet, Predicate)
Map Maps.filterKeys(Map, Predicate)Maps.filterValues(Map, Predicate)Maps.filterEntries(Map, Predicate)
SortedMap Maps.filterKeys(SortedMap, Predicate)Maps.filterValues(SortedMap, Predicate)Maps.filterEntries(SortedMap, Predicate)
Multimap Multimaps.filterKeys(Multimap, Predicate)Multimaps.filterValues(Multimap, Predicate)Multimaps.filterEntries(Multimap, Predicate)

*List的过滤视图被省略了,因为不能有效地支持类似get(int)的操作。请改用Lists.newArrayList(Collections2.filter(list, predicate))做拷贝过滤。

除了简单过滤,Guava另外提供了若干用Predicate处理Iterable的工具——通常在Iterables工具类中,或者是FluentIterable的”fluent”(链式调用)方法。

Iterables方法签名 说明 另请参见
boolean all(Iterable, Predicate) 是否所有元素满足断言?懒实现:如果发现有元素不满足,不会继续迭代 Iterators.all(Iterator, Predicate)FluentIterable.allMatch(Predicate)
boolean any(Iterable, Predicate) 是否有任意元素满足元素满足断言?懒实现:只会迭代到发现满足的元素 Iterators.any(Iterator, Predicate)FluentIterable.anyMatch(Predicate)
T find(Iterable, Predicate) 循环并返回一个满足元素满足断言的元素,如果没有则抛出NoSuchElementException Iterators.find(Iterator, Predicate)
Iterables.find(Iterable, Predicate, T default)
Iterators.find(Iterator, Predicate, T default)
Optional<T> tryFind(Iterable, Predicate) 返回一个满足元素满足断言的元素,若没有则返回Optional.absent() Iterators.find(Iterator, Predicate)
Iterables.find(Iterable, Predicate, T default)
Iterators.find(Iterator, Predicate, T default)
indexOf(Iterable, Predicate) 返回第一个满足元素满足断言的元素索引值,若没有返回-1 Iterators.indexOf(Iterator, Predicate)
removeIf(Iterable, Predicate) 移除所有满足元素满足断言的元素,实际调用Iterator.remove()方法 Iterators.removeIf(Iterator, Predicate)

函数

到目前为止,函数最常见的用途为转换集合。同样,所有的Guava转换方法也返回原集合的视图。

集合类型 转换方法
Iterable Iterables.transform(Iterable, Function)FluentIterable.transform(Function)
Iterator Iterators.transform(Iterator, Function)
Collection Collections2.transform(Collection, Function)
List Lists.transform(List, Function)
Map* Maps.transformValues(Map, Function)Maps.transformEntries(Map, EntryTransformer)
SortedMap* Maps.transformValues(SortedMap, Function)Maps.transformEntries(SortedMap, EntryTransformer)
Multimap* Multimaps.transformValues(Multimap, Function)Multimaps.transformEntries(Multimap, EntryTransformer)
ListMultimap* Multimaps.transformValues(ListMultimap, Function)Multimaps.transformEntries(ListMultimap, EntryTransformer)
Table Tables.transformValues(Table, Function)

*Map和Multimap有特殊的方法,其中有个EntryTransformer<K, V1, V2>参数,它可以使用旧的键值来计算,并且用计算结果替换旧值。

*对Set的转换操作被省略了,因为不能有效支持contains(Object)操作——译者注:懒视图实际上不会全部计算转换后的Set元素,因此不能高效地支持contains(Object)请改用Sets.newHashSet(Collections2.transform(set, function))进行拷贝转换。

01 List<String> names;
02 Map<String, Person> personWithName;
03 List<Person> people = Lists.transform(names, Functions.forMap(personWithName));
04  
05 ListMultimap<String, String> firstNameToLastNames;
06 // maps first names to all last names of people with that first name
07  
08 ListMultimap<String, String> firstNameToName = Multimaps.transformEntries(firstNameToLastNames,
09     new EntryTransformer<String, String, String> () {
10         public String transformEntry(String firstName, String lastName) {
11             return firstName + " " + lastName;
12         }
13     });

可以组合Function使用的类包括:

Ordering Ordering.onResultOf(Function)
Predicate Predicates.compose(Predicate, Function)
Equivalence Equivalence.onResultOf(Function)
Supplier Suppliers.compose(Function, Supplier)
Function Functions.compose(Function, Function)

此外,ListenableFuture API支持转换ListenableFuture。Futures也提供了接受AsyncFunction参数的方法。AsyncFunction是Function的变种,它允许异步计算值。

Futures.transform(ListenableFuture, Function)
Futures.transform(ListenableFuture, Function, Executor)
Futures.transform(ListenableFuture, AsyncFunction)
Futures.transform(ListenableFuture, AsyncFunction, Executor)

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: [Google Guava] 4-函数式编程


7

沈义扬

本站的翻译主编。关注并发编程,面向对象设计,分布式系统。

添加本文到我的收藏

guava函数式编程的更多相关文章

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

    云栖社区> 博客列表> 正文 Guava 是个风火轮之函数式编程(3)--表处理 潘家邦 2016-01-26 13:19:21 浏览1062 评论0 java Guava 摘要: 早先学 ...

  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. Quartz.NET---任务调度框架

    在我们的程序中,可能经常会遇到"每隔多久执行XXX任务"这样的问题:每天晚上24:00审核用户提交的申请:每隔1分钟去数据库中检索用户是否有新的消息:...   ...那么Quar ...

  2. HTML <a> 标签

    一,定义和用法 所有浏览器都支持 <a> 标签. <a> 标签定义超链接,用于从一张页面链接到另一张页面. <a> 元素最重要的属性是 href 属性,它指示链接的 ...

  3. 读jQuery源码 - Deferred

    Deferred首次出现在jQuery 1.5中,在jQuery 1.8之后被改写,它的出现抹平了javascript中的大量回调产生的金字塔,提供了异步编程的能力,它主要服役于jQuery.ajax ...

  4. 转 用C API 操作MySQL数据库

    用C API 操作MySQL数据库 参考MYSQL的帮助文档整理 这里归纳了C API可使用的函数,并在下一节详细介绍了它们.请参见25.2.3节,“C API函数描述”. 函数 描述 mysql_a ...

  5. Mac电脑清理硬盘"其他"

    作为一个MacBook的使用者,无不感受到苹果对于系统和硬件的完美匹配. 苹果电脑不适合玩游戏,所以我只用它开发iOS使用.电脑里除了Xcode和常用办公软件与通讯软件以外,我没有装其他的任何大应用. ...

  6. android fragment 的用法以及与activity的交互和保存数据的方法,包括屏幕切换(转载)!

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37992017 1.管理Fragment回退栈 类似与Android系统为Acti ...

  7. Android 常用抓包工具介绍之Charles

    ➠更多技术干货请戳:听云博客 Charles是一款抓包修改工具,相比起TcpDump,charles具有界面简单直观,易于上手,数据请求控制容易,修改简单,抓取数据的开始暂停方便等等优势!前面介绍了如 ...

  8. C#之 Lambda表达式

    Lambda表达式 简化了匿名委托的使用,让你让代码更加简洁,优雅.据说它是微软自c#1.0后新增的最重要的功能之一. 首先来看一下其发展 根据上面的发展历程,可以感到Lambda表达式愈加简化. 详 ...

  9. js document.createElement()的用法 (转)

    document.createElement()的用法 分析代码时,发现自己的盲点--document.createElement(),冲浪一番,总结了点经验. document.createElem ...

  10. 【转】JAVA 8 日期/时间(Date Time)API指南

    前言 本来想写下Java 8的日期/时间API,发现已经有篇不错的文章了,那就直接转载吧~ PS:主要内容没变,做了部分修改. 原文链接: journaldev 翻译: ImportNew.com - ...