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

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

Filter

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

Person.java
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  1. public class Person {
  2. public String getName() {
  3. return name;
  4. }
  5. public void setName(String name) {
  6. this.name = name;
  7. }
  8. public int getAge() {
  9. return age;
  10. }
  11. public void setAge(int age) {
  12. this.age = age;
  13. }
  14. private String name;
  15. private int age;
  16. public Person(String name, int age) {
  17. this.name = name;
  18. this.age = age;
  19. }
  20. }

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

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

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

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

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

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

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  1. List<Person> oldPeople = new ArrayList<Person>();
  2. for (Person person : people) {
  3. if (person.getAge() >= 20) {
  4. oldPeople.add(person);
  5. }
  6. }

这就是典型的filter模式。filter即从一个集合中根据一个条件筛选元素。其中person.getAge() >=20就是这个条件。Guava为这种模式提供了一个filter的方法。所以我们可以这样写。

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

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

Predicate.java
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  1. @GwtCompatible
  2. public interface Predicate<T> {
  3. /**
  4. * Returns the result of applying this predicate to {@code input}. This method is <i>generally
  5. * expected</i>, but not absolutely required, to have the following properties:
  6. *
  7. * <ul>
  8. * <li>Its execution does not cause any observable side effects.
  9. * <li>The computation is <i>consistent with equals</i>; that is, {@link Objects#equal
  10. * Objects.equal}{@code (a, b)} implies that {@code predicate.apply(a) ==
  11. * predicate.apply(b))}.
  12. * </ul>
  13. *
  14. * @throws NullPointerException if {@code input} is null and this predicate does not accept null
  15. * arguments
  16. */
  17. boolean apply(@Nullable T input);
  18. /**
  19. * Indicates whether another object is equal to this predicate.
  20. *
  21. * <p>Most implementations will have no reason to override the behavior of {@link Object#equals}.
  22. * However, an implementation may also choose to return {@code true} whenever {@code object} is a
  23. * {@link Predicate} that it considers <i>interchangeable</i> with this one. "Interchangeable"
  24. * <i>typically</i> means that {@code this.apply(t) == that.apply(t)} for all {@code t} of type
  25. * {@code T}). Note that a {@code false} result from this method does not imply that the
  26. * predicates are known <i>not</i> to be interchangeable.
  27. */
  28. @Override
  29. boolean equals(@Nullable Object object);
  30. }

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

And Predicate

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

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

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

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

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

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  1. private Predicate<Person> ageBiggerThan(final int age) {
  2. return new Predicate<Person>() {
  3. public boolean apply(Person person) {
  4. return person.getAge() >= age;
  5. }
  6. };
  7. }
  8. private Predicate<Person> nameContains(final String str) {
  9. return new Predicate<Person>() {
  10. public boolean apply(Person person) {
  11. return person.getName().contains(str);
  12. }
  13. };
  14. }

而我们的结果其实就是这两个Predicate相与。Guava给我们提供了and方法,用于对一组Predicate求与。

  1. 1
  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. 1
  2. 2
  3. 3
  4. 4
  1. List<String> names = new ArrayList<String>();
  2. for (Person person : people) {
  3. names.add(person.getName());
  4. }

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

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

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

Function.java
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  1. @GwtCompatible
  2. public interface Function<F, T> {
  3. /**
  4. * Returns the result of applying this function to {@code input}. This method is <i>generally
  5. * expected</i>, but not absolutely required, to have the following properties:
  6. *
  7. * <ul>
  8. * <li>Its execution does not cause any observable side effects.
  9. * <li>The computation is <i>consistent with equals</i>; that is, {@link Objects#equal
  10. * Objects.equal}{@code (a, b)} implies that {@code Objects.equal(function.apply(a),
  11. * function.apply(b))}.
  12. * </ul>
  13. *
  14. * @throws NullPointerException if {@code input} is null and this function does not accept null
  15. * arguments
  16. */
  17. @Nullable T apply(@Nullable F input);
  18. /**
  19. * Indicates whether another object is equal to this function.
  20. *
  21. * <p>Most implementations will have no reason to override the behavior of {@link Object#equals}.
  22. * However, an implementation may also choose to return {@code true} whenever {@code object} is a
  23. * {@link Function} that it considers <i>interchangeable</i> with this one. "Interchangeable"
  24. * <i>typically</i> means that {@code Objects.equal(this.apply(f), that.apply(f))} is true for all
  25. * {@code f} of type {@code F}. Note that a {@code false} result from this method does not imply
  26. * that the functions are known <i>not</i> to be interchangeable.
  27. */
  28. @Override
  29. boolean equals(@Nullable Object object);
  30. }

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

reduce

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

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

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

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

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

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

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

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

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

Fluent pattern

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

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  1. private Function<Person, String> getName() {
  2. return new Function<Person, String>() {
  3. public String apply( Person person) {
  4. return person.getName();
  5. }
  6. };
  7. }
  8. public void getPeopleNamesByAge() {
  9. List<String> names = newArrayList(transform(filter(people, ageBiggerThan(20)), getName()));
  10. }

这样括号套括号的着实不好看。能不能改进一下那?Guava为我们提供了fluent模式的API,我们可以这样来写。

  1. 1
  1. List<String> names = from(people).filter(ageBiggerThan(20)).transform(getName()).toList();

Guava中还有很多好玩的东西,大家时间可以多发掘发掘。这篇文章的源码已经被我放置到github中,感兴趣的可以自行查看。

Java经典类库-Guava中的函数式编程讲解的更多相关文章

  1. Java 中的函数式编程(Functional Programming):Lambda 初识

    Java 8 发布带来的一个主要特性就是对函数式编程的支持. 而 Lambda 表达式就是一个新的并且很重要的一个概念. 它提供了一个简单并且很简洁的编码方式. 首先从几个简单的 Lambda 表达式 ...

  2. C#中面向对象编程中的函数式编程详解

    介绍 使用函数式编程来丰富面向对象编程的想法是陈旧的.将函数编程功能添加到面向对象的语言中会带来面向对象编程设计的好处. 一些旧的和不太老的语言,具有函数式编程和面向对象的编程: 例如,Smallta ...

  3. Apache Beam中的函数式编程理念

    不多说,直接上干货! Apache Beam中的函数式编程理念 Apache Beam的编程范式借鉴了函数式编程的概念,从工程和实现角度向命令式妥协. 编程的领域里有三大流派:函数式.命令式.逻辑式. ...

  4. 可爱的 Python : Python中的函数式编程,第三部分

    英文原文:Charming Python: Functional programming in Python, Part 3,翻译:开源中国 摘要:  作者David Mertz在其文章<可爱的 ...

  5. C#中的函数式编程:序言(一)

    学了那么久的函数式编程语言,一直想写一些相关的文章.经过一段时间的考虑,我决定开这个坑. 至于为什么选择C#,在我看来,编程语言分三类:一类是难以进行函数式编程的语言,这类语言包括Java6.C语言等 ...

  6. (数据科学学习手札48)Scala中的函数式编程

    一.简介 Scala作为一门函数式编程与面向对象完美结合的语言,函数式编程部分也有其独到之处,本文就将针对Scala中关于函数式编程的一些常用基本内容进行介绍: 二.在Scala中定义函数 2.1 定 ...

  7. C#中的函数式编程:递归与纯函数(二) 学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面

    C#中的函数式编程:递归与纯函数(二)   在序言中,我们提到函数式编程的两大特征:无副作用.函数是第一公民.现在,我们先来深入第一个特征:无副作用. 无副作用是通过引用透明(Referential ...

  8. Java中的函数式编程(二)函数式接口Functional Interface

    写在前面 前面说过,判断一门语言是否支持函数式编程,一个重要的判断标准就是:它是否将函数看做是"第一等公民(first-class citizens)".函数是"第一等公 ...

  9. Java中的函数式编程(七)流Stream的Map-Reduce操作

    写在前面 Stream 的 Map-Reduce 操作是Java 函数式编程的精华所在,同时也是最为复杂的部分.但一旦你啃下了这块硬骨头,那你就真正熟悉Java的函数式编程了. 如果你有大数据的编程经 ...

随机推荐

  1. VC++ CButton::SetCheck 的使用方法

    CButton::SetCheck void SetCheck(int nCheck); 参数 nCheck 指定检查状态. 此参数可以是下列值之一: 值                        ...

  2. js中函数的定义

  3. [2015hdu多校联赛补题]hdu5302 Connect the Graph

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5302 题意:给你一个无向图,它的边要么是黑色要么是白色,且图上的每个点最多与两个黑边两个白边相连.现在 ...

  4. css小tip

    1. <input>标签的默认样式 当在页面中添加一个input标签,当点击输入框时会有一个外边框包裹着,可以使用 : input { outline: none} 去除点击时产生的外边框 ...

  5. Light OJ 1019 - Brush (V)(图论-dijkstra)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1019 题目大意:Tanvir想从节点1的位置走到节点n的位置, 输出最短距离, ...

  6. Oracle基础知识笔记

    1.打开oracle相关服务 2.创建Oracle用户 create user 用户名 identified by 密码;(需要dba角色创建) 3.权限管理 (1)添加权限 grant  权限.角色 ...

  7. jsp学习--JavaBean定义和在Jsp中使用JavaBean

    一.JavaBean 1.什么是JavaBean JavaBean是一个遵循特定写法的Java类,它通常具有如下特点:>>这个Java类必须具有一个无参的构造函数>>属性必须私 ...

  8. iOS报错笔记

    问题一: linker command failed with exit code 1 (use -vto see invocation) 原因:导入了.m的头文件,导致同时有两个一样的.m文件在编译 ...

  9. JavaScript-indexOf函数

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  10. 斯坦福第四课:多变量线性回归(Linear Regression with Multiple Variables)

    4.1  多维特征 4.2  多变量梯度下降 4.3  梯度下降法实践 1-特征缩放 4.4  梯度下降法实践 2-学习率 4.5  特征和多项式回归 4.6  正规方程 4.7  正规方程及不可逆性 ...