guava
如果我要新建一个java的项目,那么有两个类库是必备的,一个是junit,另一个是Guava。选择junit,因为我喜欢TDD,喜欢自动化测试。而是用Guava,是因为我喜欢简洁的API。Guava提供了很多的实用工具函数来弥补java标准库的不足,另外Guava还引入了函数式编程的概念,在一定程度上缓解了java在JDK1.8之前没有lambda的缺陷,使使用java书写简洁易读的函数式风格的代码成为可能。
下面就简单的介绍下Guava中的一些体现了函数式编程的API。
Filter
我们先创建一个简单的Person类。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | publicclassPerson {    publicString getName() {        returnname;    }    publicvoidsetName(String name) {        this.name = name;    }    publicintgetAge() {        returnage;    }    publicvoidsetAge(intage) {        this.age = age;    }    privateString name;    privateintage;    publicPerson(String name, intage) {        this.name = name;        this.age = age;    }} | 
如果要产生一个Person类的List,通常的写法可能是这样子。
| 1 2 3 4 5 | List<Person> people = newArrayList<Person>();        people.add(newPerson("bowen",27));        people.add(newPerson("bob", 20));        people.add(newPerson("Katy", 18));        people.add(newPerson("Logon", 24)); | 
而Guava提供了一个newArrayList的方法,其自带类型推演,并可以方便的生成一个List,并且通过参数传递初始化值。
| 1 2 3 4 | List<Person> people = newArrayList(newPerson("bowen", 27),               newPerson("bob", 20),               newPerson("Katy", 18),               newPerson("Logon", 24)); | 
当然,这不算函数式编程的范畴,这是Guava给我们提供的一个实用的函数。
如果我们选取其中年龄大于20的人,通常的写法可能是这样子。
| 1 2 3 4 5 6 | List<Person> oldPeople = newArrayList<Person>();       for(Person person : people) {           if(person.getAge() >= 20) {               oldPeople.add(person);           }       } | 
这就是典型的filter模式。filter即从一个集合中根据一个条件筛选元素。其中person.getAge() >=20就是这个条件。Guava为这种模式提供了一个filter的方法。所以我们可以这样写。
| 1 2 3 4 5 | List<Person> oldPeople = newArrayList(filter(people, newPredicate<Person>() {           publicbooleanapply(Person person) {               returnperson.getAge() >= 20;           }       })); | 
这里的Predicate是Guava中的一个接口,我们来看看它的定义。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | @GwtCompatiblepublicinterfacePredicate<T> {  /**   * Returns the result of applying this predicate to {@code input}. This method is <i>generally   * expected</i>, but not absolutely required, to have the following properties:   *   * <ul>   * <li>Its execution does not cause any observable side effects.   * <li>The computation is <i>consistent with equals</i>; that is, {@link Objects#equal   *     Objects.equal}{@code (a, b)} implies that {@code predicate.apply(a) ==   *     predicate.apply(b))}.   * </ul>   *   * @throws NullPointerException if {@code input} is null and this predicate does not accept null   *     arguments   */  booleanapply(@NullableT input);  /**   * Indicates whether another object is equal to this predicate.   *   * <p>Most implementations will have no reason to override the behavior of {@link Object#equals}.   * However, an implementation may also choose to return {@code true} whenever {@code object} is a   * {@link Predicate} that it considers <i>interchangeable</i> with this one. "Interchangeable"   * <i>typically</i> means that {@code this.apply(t) == that.apply(t)} for all {@code t} of type   * {@code T}). Note that a {@code false} result from this method does not imply that the   * predicates are known <i>not</i> to be interchangeable.   */  @Override  booleanequals(@NullableObject object);} | 
里面只有一个apply方法,接收一个泛型的实参,返回一个boolean值。由于java世界中函数并不是一等公民,所以我们无法直接传递一个条件函数,只能通过Predicate这个类包装一下。
And Predicate
如果要再实现一个方法来查找People列表中所有名字中包含b字母的列表,我们可以用Guava简单的实现。
| 1 2 3 4 5 | List<Person> namedPeople = newArrayList(filter(people, newPredicate<Person>() {            publicbooleanapply(Person person) {                returnperson.getName().contains("b");            }        })); | 
一切是这么的简单。 那么新需求来了,如果现在需要找年龄>=20并且名称包含b的人,该如何实现那? 可能你会这样写。
| 1 2 3 4 5 | List<Person> filteredPeople = newArrayList(filter(people, newPredicate<Person>() {           publicbooleanapply(Person person) {               returnperson.getName().contains("b") && person.getAge() >= 20;           }       })); | 
这样写的话就有一定的代码重复,因为之前我们已经写了两个Predicate来分别实现这两个条件判断,能不能重用之前的Predicate那?答案是能。 我们首先将之前生成年龄判断和名称判断的两个Predicate抽成方法。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | privatePredicate<Person> ageBiggerThan(finalintage) {        returnnewPredicate<Person>() {            publicbooleanapply(Person person) {                returnperson.getAge() >= age;            }        };    }privatePredicate<Person> nameContains(finalString str) {        returnnewPredicate<Person>() {            publicbooleanapply(Person person) {                returnperson.getName().contains(str);            }        };    } | 
而我们的结果其实就是这两个Predicate相与。Guava给我们提供了and方法,用于对一组Predicate求与。
| 1 | List<Person> filteredPeople = newArrayList(filter(people, and(ageBiggerThan(20), nameContains("b")))); | 
由于and接收一组Predicate,返回也是一个Predicate,所以可以直接作为filter的第二个参数。如果不熟悉函数式编程的人可能感觉有点怪异,但是习惯了就会觉得它的强大与简洁。 当然除了and,Guava还为我们提供了or,用于对一组Predicate求或。这里就不多讲了,大家可以自己练习下。
Map(transform)
列表操作还有另一个常见的模式,就是将数组中的所有元素映射为另一种元素的列表,这就是map pattern。举个例子,求People列表中的所有人名。程序员十有八九都会这样写。
| 1 2 3 4 | List<String> names = newArrayList<String>();       for(Person person : people) {           names.add(person.getName());       } | 
Guava已经给我们提供了这种Pattern的结果办法,那就是使用transform方法。
| 1 2 3 4 5 | List<String> names = newArrayList(transform(people, newFunction<Person, String>() {            publicString apply( Person person) {                returnperson.getName();            }        })); | 
Function是另外一种用于封装函数的接口对象。它的定义如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | @GwtCompatiblepublicinterfaceFunction<F, T> {  /**   * Returns the result of applying this function to {@code input}. This method is <i>generally   * expected</i>, but not absolutely required, to have the following properties:   *   * <ul>   * <li>Its execution does not cause any observable side effects.   * <li>The computation is <i>consistent with equals</i>; that is, {@link Objects#equal   *     Objects.equal}{@code (a, b)} implies that {@code Objects.equal(function.apply(a),   *     function.apply(b))}.   * </ul>   *   * @throws NullPointerException if {@code input} is null and this function does not accept null   *     arguments   */  @NullableT apply(@NullableF input);  /**   * Indicates whether another object is equal to this function.   *   * <p>Most implementations will have no reason to override the behavior of {@link Object#equals}.   * However, an implementation may also choose to return {@code true} whenever {@code object} is a   * {@link Function} that it considers <i>interchangeable</i> with this one. "Interchangeable"   * <i>typically</i> means that {@code Objects.equal(this.apply(f), that.apply(f))} is true for all   * {@code f} of type {@code F}. Note that a {@code false} result from this method does not imply   * that the functions are known <i>not</i> to be interchangeable.   */  @Override  booleanequals(@NullableObject object);} | 
它与Predicate非常相似,但不同的是它接收两个泛型,apply方法接收一种泛型实参,返回值是另一种泛型值。正是这个apply方法定义了数组间元素一对一的map规则。
reduce
除了filter与map模式外,列表操作还有一种reduce操作。比如求people列表中所有人年龄的和。Guava并未提供reduce方法。具体原因我们并不清楚。但是我们可以自己简单的实现一个reduce pattern。 先定义一个Func的接口。
| 1 2 3 4 5 | publicinterfaceFunc<F,T> {         T apply(F currentElement, T origin);     } | 
apply方法的第一个参数为列表中的当前元素,第二个参数为默认值,返回值类型为默认值类型。 然后我们定义个reduce的静态方法。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | publicclassReduce {    privateReduce() {    }    publicstatic<F,T> T reduce(finalIterable<F> iterable, finalFunc<F, T> func, T origin) {        for(Iterator iterator = iterable.iterator(); iterator.hasNext(); ) {            origin = func.apply((F)(iterator.next()), origin);        }        returnorigin;    }} | 
reduce方法接收三个参数,第一个是需要进行reduce操作的列表,第二个是封装reduce操作的Func,第三个参数是初始值。
我们可以使用这个reduce来实现求people列表中所有人的年龄之和。
| 1 2 3 4 5 6 | Integer ages = Reduce.reduce(people, newFunc<Person, Integer>() {            publicInteger apply(Person person, Integer origin) {                returnperson.getAge() + origin;            }        }, 0); | 
我们也可以轻松的写一个方法来得到年龄的最大值。
| 1 2 3 4 5 6 | Integer maxAge = Reduce.reduce(people, newFunc<Person, Integer>() {            publicInteger apply(Person person, Integer origin) {                returnperson.getAge() > origin ? person.getAge() : origin;            }        }, 0); | 
Fluent pattern
现在新需求来了,需要找出年龄>=20岁的人的所有名称。该如何操作那?我们可以使用filter过滤出年龄>=20的人,然后使用transform得到剩下的所有人的人名。
| 1 2 3 4 5 6 7 8 9 10 11 12 | privateFunction<Person, String> getName() {        returnnewFunction<Person, String>() {            publicString apply( Person person) {                returnperson.getName();            }        };    }    publicvoidgetPeopleNamesByAge() {        List<String> names = newArrayList(transform(filter(people, ageBiggerThan(20)), getName()));    } | 
这样括号套括号的着实不好看。能不能改进一下那?Guava为我们提供了fluent模式的API,我们可以这样来写。
| 1 | List<String> names = from(people).filter(ageBiggerThan(20)).transform(getName()).toList(); | 
Guava中还有很多好玩的东西,大家时间可以多发掘发掘。这篇文章的源码已经被我放置到github中,感兴趣的可以自行查看。
guava的更多相关文章
- Spring cache简单使用guava cache
		Spring cache简单使用 前言 spring有一套和各种缓存的集成方式.类似于sl4j,你可以选择log框架实现,也一样可以实现缓存实现,比如ehcache,guava cache. [TOC ... 
- Guava库介绍之实用工具类
		作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 本文是我写的Google开源的Java编程库Guava系列之一,主要介 ... 
- Google Java编程库Guava介绍
		本系列想介绍下Java下开源的优秀编程库--Guava[ˈgwɑːvə].它包含了Google在Java项目中使用一些核心库,包含集合(Collections),缓存(Caching),并发编程库(C ... 
- [Java 缓存] Java Cache之 Guava Cache的简单应用.
		前言 今天第一次使用MarkDown的形式发博客. 准备记录一下自己对Guava Cache的认识及项目中的实际使用经验. 一: 什么是Guava Guava工程包含了若干被Google的 Java项 ... 
- [转载]Google Guava官方教程(中文版)
		原文链接 译文链接 译者: 沈义扬,罗立树,何一昕,武祖 校对:方腾飞 引言 Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如:集合 [collections] ... 
- java开发人员,最应该学习和熟练使用的工具类。google guava.(谷歌 瓜娃)
		学习参考文章: http://blog.csdn.net/wisgood/article/details/13297535 http://ifeve.com/google-guava/ http:// ... 
- Guava学习笔记(一)概览
		Guava是谷歌开源的一套Java开发类库,以简洁的编程风格著称,提供了很多实用的工具类, 在之前的工作中应用过Collections API和Guava提供的Cache,不过对Guava没有一个系统 ... 
- Guava monitor
		Guava的com.google.util.concurrent类库提供了相对于jdk java.util.concurrent包更加方便实用的并发类,Monitor类就是其中一个.Monitor类在 ... 
- 使用Guava EventBus构建publish/subscribe系统
		Google的Guava类库提供了EventBus,用于提供一套组件内publish/subscribe的解决方案.事件总线EventBus,用于管理事件的注册和分发.在系统中,Subscribers ... 
- Guava Supplier实例
		今天想讲一下Guava Suppliers的几点用法.Guava Suppliers的主要功能是创建包裹的单例对象,通过get方法可以获取对象的值.每次获取的对象都为同一个对象,但你和单例模式有所区别 ... 
随机推荐
- ArcGIS JS 学习笔记2 实现仿百度的拖拽画圆
			一.前言 吐槽一下,百度在国内除了百度地图是良心产品外,其他的真的不敢恭维.在上一篇笔记里,我已经实现了自定义的地图测量模块.在百度地图里面(其他地图)都有一个周边搜索的功能,拖拽画一个圆,然后以圆半 ... 
- ARP投毒及其防御方法
			1.攻击原理 ARP欺骗就是中间人欺骗pc机,告诉pc机它是服务器.再欺骗服务器,告诉服务器它就是pc机.以致获取服务器与pc机的会话信息. 中间人欺骗服务器时,会给服务器发一个报文,发之前把报文中的 ... 
- SharePoint 2013 Error - File names can't contain the following characters: & " ? < > # {} % ~ / \.
			错误截图: 错误信息: --------------------------- Message from webpage --------------------------- File names ... 
- 学习 Docker - 入门
			Docker简介 一种虚拟容器技术. 一种虚拟化分方案: 操作系统级别的虚拟化: 只能运行相同或相似内核的操作系统: 依赖与linux内核特性:Namespace和Cgroups(Control Gr ... 
- php示例代码之empty函数
			1 2 3 4 5 6 7 8 9 10 11 <?php $testVar=0; if(empty($testVar)) { echo 'msg:true'; } ... 
- WPF学习之路(八)页面
			传统的应用程序中有两类应用程序模式:桌面应用,Web应用.WPF的导航应用程序模糊了这两类应用程序的界限的第三类应用程序 WPF导航表现为两种形式,一是将导航内容寄宿于窗口,二是XAML浏览器应用程序 ... 
- MongoDB-安装&启动
			MongoDB安装 使用的版本为3.0,虚拟机操作系统为rhel-server-7.0-x86_641. 将mongodb-linux-x86_64-rhel70-3.0.11.tgz上传并解压到 / ... 
- Linux 折腾汇集,实时更新
			一.Linux教程 入门教程:http://www.92csz.com/study/linux/ 命令大全:http://man.linuxde.net/ 一.界面: 在Ubuntu.Linux Mi ... 
- EnumMap
			以下内容基于jdk1.7.0_79源码: 什么是EnumMap Map接口的实现,其key-value映射中的key是Enum类型: 补充说明 其原理就是一个对象数组,数组的下标索引就是根据Map中的 ... 
- MySQL online ddl原理
			背景 dba的日常工作肯定有一项是ddl变更,ddl变更会锁表,这个可以说是dba心中永远的痛,特别是执行ddl变更,导致库上大量线程处于“Waiting for meta data lock”状态的 ... 
