原文出处: 黄博文

如果我要新建一个java的项目,那么有两个类库是必备的,一个是junit,另一个是Guava。选择junit,因为我喜欢TDD,喜欢自动化测试。而是用Guava,是因为我喜欢简洁的API。Guava提供了很多的实用工具函数来弥补java标准库的不足,另外Guava还引入了函数式编程的概念,在一定程度上缓解了java在JDK1.8之前没有lambda的缺陷,使使用java书写简洁易读的函数式风格的代码成为可能。

下面就简单的介绍下Guava中的一些体现了函数式编程的API。

Filter

我们先创建一个简单的Person类。

Person.java
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
public class Person {
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    private String name;
    private int age;
 
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

如果要产生一个Person类的List,通常的写法可能是这样子。

1
2
3
4
5
List<Person> people = new ArrayList<Person>();
        people.add(new Person("bowen",27));
        people.add(new Person("bob", 20));
        people.add(new Person("Katy", 18));
        people.add(new Person("Logon", 24));

Guava提供了一个newArrayList的方法,其自带类型推演,并可以方便的生成一个List,并且通过参数传递初始化值。

1
2
3
4
List<Person> people = newArrayList(new Person("bowen", 27),
               new Person("bob", 20),
               new Person("Katy", 18),
               new Person("Logon", 24));

当然,这不算函数式编程的范畴,这是Guava给我们提供的一个实用的函数。

如果我们选取其中年龄大于20的人,通常的写法可能是这样子。

1
2
3
4
5
6
List<Person> oldPeople = new ArrayList<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, new Predicate<Person>() {
           public boolean apply(Person person) {
               return person.getAge() >= 20;
           }
       }));

这里的Predicate是Guava中的一个接口,我们来看看它的定义。

Predicate.java
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
@GwtCompatible
public interface Predicate<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
   */
  boolean apply(@Nullable T 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
  boolean equals(@Nullable Object object);
}

里面只有一个apply方法,接收一个泛型的实参,返回一个boolean值。由于java世界中函数并不是一等公民,所以我们无法直接传递一个条件函数,只能通过Predicate这个类包装一下。

And Predicate

如果要再实现一个方法来查找People列表中所有名字中包含b字母的列表,我们可以用Guava简单的实现。

1
2
3
4
5
List<Person> namedPeople = newArrayList(filter(people, new Predicate<Person>() {
            public boolean apply(Person person) {
                return person.getName().contains("b");
            }
        }));

一切是这么的简单。 那么新需求来了,如果现在需要找年龄>=20并且名称包含b的人,该如何实现那? 可能你会这样写。

1
2
3
4
5
List<Person> filteredPeople = newArrayList(filter(people, new Predicate<Person>() {
           public boolean apply(Person person) {
               return person.getName().contains("b") && person.getAge() >= 20;
           }
       }));

这样写的话就有一定的代码重复,因为之前我们已经写了两个Predicate来分别实现这两个条件判断,能不能重用之前的Predicate那?答案是能。 我们首先将之前生成年龄判断和名称判断的两个Predicate抽成方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private Predicate<Person> ageBiggerThan(final int age) {
        return new Predicate<Person>() {
            public boolean apply(Person person) {
                return person.getAge() >= age;
            }
        };
    }
 
private Predicate<Person> nameContains(final String str) {
        return new Predicate<Person>() {
            public boolean apply(Person person) {
                return person.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 = new ArrayList<String>();
       for (Person person : people) {
           names.add(person.getName());
       }

Guava已经给我们提供了这种Pattern的结果办法,那就是使用transform方法。

1
2
3
4
5
List<String> names = newArrayList(transform(people, new Function<Person, String>() {
            public String apply( Person person) {
                return person.getName();
            }
        }));

Function是另外一种用于封装函数的接口对象。它的定义如下:

Function.java
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
@GwtCompatible
public interface Function<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
   */
  @Nullable T apply(@Nullable F 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
  boolean equals(@Nullable Object object);
}

它与Predicate非常相似,但不同的是它接收两个泛型,apply方法接收一种泛型实参,返回值是另一种泛型值。正是这个apply方法定义了数组间元素一对一的map规则。

reduce

除了filter与map模式外,列表操作还有一种reduce操作。比如求people列表中所有人年龄的和。Guava并未提供reduce方法。具体原因我们并不清楚。但是我们可以自己简单的实现一个reduce pattern。 先定义一个Func的接口。

Func.java
1
2
3
4
5
public interface Func<F,T> {
 
         T apply(F currentElement, T origin);
 
     }

apply方法的第一个参数为列表中的当前元素,第二个参数为默认值,返回值类型为默认值类型。 然后我们定义个reduce的静态方法。

Reduce.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Reduce {
    private Reduce() {
 
    }
 
    public static <F,T> T reduce(final Iterable<F> iterable, final Func<F, T> func, T origin) {
 
        for (Iterator iterator = iterable.iterator(); iterator.hasNext(); ) {
            origin = func.apply((F)(iterator.next()), origin);
        }
 
        return origin;
    }
}

reduce方法接收三个参数,第一个是需要进行reduce操作的列表,第二个是封装reduce操作的Func,第三个参数是初始值。

我们可以使用这个reduce来实现求people列表中所有人的年龄之和。

1
2
3
4
5
6
Integer ages = Reduce.reduce(people, new Func<Person, Integer>() {
 
            public Integer apply(Person person, Integer origin) {
                return person.getAge() + origin;
            }
        }, 0);

我们也可以轻松的写一个方法来得到年龄的最大值。

1
2
3
4
5
6
Integer maxAge = Reduce.reduce(people, new Func<Person, Integer>() {
 
            public Integer apply(Person person, Integer origin) {
                return person.getAge() > origin ? person.getAge() : origin;
            }
        }, 0);

Fluent pattern

现在新需求来了,需要找出年龄>=20岁的人的所有名称。该如何操作那?我们可以使用filter过滤出年龄>=20的人,然后使用transform得到剩下的所有人的人名。

1
2
3
4
5
6
7
8
9
10
11
12
private Function<Person, String> getName() {
        return new Function<Person, String>() {
            public String apply( Person person) {
                return person.getName();
            }
        };
    }
 
    public void getPeopleNamesByAge() {
 
        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的更多相关文章

  1. Spring cache简单使用guava cache

    Spring cache简单使用 前言 spring有一套和各种缓存的集成方式.类似于sl4j,你可以选择log框架实现,也一样可以实现缓存实现,比如ehcache,guava cache. [TOC ...

  2. Guava库介绍之实用工具类

    作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 本文是我写的Google开源的Java编程库Guava系列之一,主要介 ...

  3. Google Java编程库Guava介绍

    本系列想介绍下Java下开源的优秀编程库--Guava[ˈgwɑːvə].它包含了Google在Java项目中使用一些核心库,包含集合(Collections),缓存(Caching),并发编程库(C ...

  4. [Java 缓存] Java Cache之 Guava Cache的简单应用.

    前言 今天第一次使用MarkDown的形式发博客. 准备记录一下自己对Guava Cache的认识及项目中的实际使用经验. 一: 什么是Guava Guava工程包含了若干被Google的 Java项 ...

  5. [转载]Google Guava官方教程(中文版)

      原文链接  译文链接 译者: 沈义扬,罗立树,何一昕,武祖  校对:方腾飞 引言 Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如:集合 [collections] ...

  6. java开发人员,最应该学习和熟练使用的工具类。google guava.(谷歌 瓜娃)

    学习参考文章: http://blog.csdn.net/wisgood/article/details/13297535 http://ifeve.com/google-guava/ http:// ...

  7. Guava学习笔记(一)概览

    Guava是谷歌开源的一套Java开发类库,以简洁的编程风格著称,提供了很多实用的工具类, 在之前的工作中应用过Collections API和Guava提供的Cache,不过对Guava没有一个系统 ...

  8. Guava monitor

    Guava的com.google.util.concurrent类库提供了相对于jdk java.util.concurrent包更加方便实用的并发类,Monitor类就是其中一个.Monitor类在 ...

  9. 使用Guava EventBus构建publish/subscribe系统

    Google的Guava类库提供了EventBus,用于提供一套组件内publish/subscribe的解决方案.事件总线EventBus,用于管理事件的注册和分发.在系统中,Subscribers ...

  10. Guava Supplier实例

    今天想讲一下Guava Suppliers的几点用法.Guava Suppliers的主要功能是创建包裹的单例对象,通过get方法可以获取对象的值.每次获取的对象都为同一个对象,但你和单例模式有所区别 ...

随机推荐

  1. 解决Visual Stuido 2013中Xamarin的*.axml文件没有智能提示问题

    最近发现VS2013开发Android,发现.axml无法进行智能提示. 1.打开VS2013菜单栏中,点击工具--选项--文本编辑器--文件扩展名 2.增加一个扩展名为"axml" ...

  2. SrsDataConnector The SQL Server Reporting Services account is a local user and is not supported.

    这次使用OS+SQL的镜像还原系统后安装了CRM 2015,主要流程是 安装IIS/AD,SSRS ,CRM2015.自带的SQL中SSRS没有安装完全,需配置一下. 这一切都满顺利的,最后在安装 S ...

  3. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  4. xcode 7及以上版本网络请求不成功的原因

    在Xcode 7以前的版本使用的是http协议,从Xcode 7开始,默认使用的是https协议,这优化了加密性能. 要想在Xcode 7及以上版本能够进行网络请求,我们需要在info.plist文件 ...

  5. Android网络请求框架

    本篇主要介绍一下Android中经常用到的网络请求框架: 客户端网络请求,就是客户端发起网络请求,经过网络框架的特殊处理,让后将请求发送的服务器,服务器根据 请求的参数,返回客户端需要的数据,经过网络 ...

  6. 截取UIImage指定大小区域

    截取UIImage指定大小区域 最近遇到这样的需求:从服务器获取到一张照片,只需要显示他的左半部分,或者中间部分等等.也就是截取UIImage指定大小区域. UIImage扩展 我的解决方案是对UII ...

  7. ionic教程之Win10环境下ionic+angular实现滑动菜单及列表

    写博客,不容易,你们的评论和转载,就是我的动力,但请注明出处,隔壁老王的开发园:http://www.cnblogs.com/titibili/p/5124940.html 2016年1月11日 21 ...

  8. php中的cookie用法

    cookie和session都可以暂时保存在多个页面中使用的变量,但是它们有本质的差别. cookie存放在客户端浏览器中,session保存在服务器上.它们之间的联系是session ID一般保存在 ...

  9. HashMap常用方法

    当需要对元素进行计数时,HashMap非常有用,如下例子,统计一个字符串中每个字符出现的次数: package simplejava; import java.util.HashMap; import ...

  10. Virtual Box 杂记

    1. Virtual Box后台运行 a. VBoxManage startvm yourvmname --type headlessb. VBoxHeadless --startvm yourvmn ...