java8-谓词(predicate)

栏目: 编程语言 · Java · 发布时间: 5年前

内容简介:我们首先看一个例子,假设你有一个 Apple 类,它有一个getColor方法,还有一个变量inventory保存着一个Apples的列表。你可能想要选出所有的绿苹果,并返回一个列表。通常我们用筛选(filter)一词来表达这个概念。在Java 8之前,你可能会写这样一个方法 filterGreenApples :但是接下来,有人可能想要选出重的苹果,比如超过150克,于是你心情沉重地写了下面这个方法,甚至用了复制粘贴:

传递代码

我们首先看一个例子,假设你有一个 Apple 类,它有一个getColor方法,还有一个变量inventory保存着一个Apples的列表。你可能想要选出所有的绿苹果,并返回一个列表。通常我们用筛选(filter)一词来表达这个概念。在 Java 8之前,你可能会写这样一个方法 filterGreenApples :

public static List<Apple> filterGreenApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if ("green".equals(apple.getColor())) {
result.add(apple);
        }
    }
return result;
}

但是接下来,有人可能想要选出重的苹果,比如超过150克,于是你心情沉重地写了下面这

个方法,甚至用了复制粘贴:

public static List<Apple> filterHeavyApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (apple.getWeight() > 150) {
result.add(apple);
        }      
    }
return result;
}

我们都知道软件工程中复制粘贴的危险——给一个做了更新和修正,却忘了另一个。嘿,这

两个方法只有一行不同: if 里面高亮的那行条件。如果这两个高亮的方法之间的差异仅仅是接受

的重量范围不同,那么你只要把接受的重量上下限作为参数传递给 filter 就行了,比如指定

(150, 1000) 来选出重的苹果(超过150克),或者指定 (0, 80) 来选出轻的苹果(低于80克)。

但是,我们前面提过了,Java 8会把条件代码作为参数传递进去,这样可以避免 filter 方法

出现重复的代码。现在你可以写:

public static boolean isGreenApple(Apple apple) {
    return "green".equals(apple.getColor());
}
public static boolean isHeavyApple(Apple apple) {
    return apple.getWeight() > 150;
}
static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple: inventory){
        if (p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

要用它的话,你可以写:

filterApples(inventory, Apple::isGreenApple);

或者

filterApples(inventory, Apple::isHeavyApple);

什么是谓词?

前面的代码传递了方法 Apple::isGreenApple (它接受参数 Apple 并返回一个

boolean )给 filterApples ,后者则希望接受一个 Predicate<Apple> 参数。词 谓词(predicate)

在数学上常常用来代表一个类似函数的东西,它接受一个参数值,并返回 true 或 false 。你

在后面会看到,Java 8也会允许你写 Function<Apple,Boolean> ——在学校学过函数却没学

过谓词的读者对此可能更熟悉,但用 Predicate<Apple> 是更标准的方式,效率也会更高一

点儿,这避免了把 boolean 封装在 Boolean 里面。

从传递方法到 Lambda

把方法作为值来传递显然很有用,但要是为类似于 isHeavyApple 和 isGreenApple 这种可

能只用一两次的短方法写一堆定义有点儿烦人。不过Java 8也解决了这个问题,它引入了一套新

记法(匿名函数或Lambda),让你可以写

filterApples(inventory, (Apple a) -> "green".equals(a.getColor()) );

或者

filterApples(inventory, (Apple a) -> a.getWeight() > 150 );

甚至

filterApples(inventory, (Apple a) -> a.getWeight() < 80 ||

"brown".equals(a.getColor()) );

完整的代码为:

public class FilteringApples1 {
    public static void main(String[] args) {
        List<FilteringApples1.Apple> inventory = Arrays.asList(new FilteringApples1.Apple(80, "green"),
                new FilteringApples1.Apple(155, "green"),
                new FilteringApples1.Apple(120, "red"));

        List<FilteringApples1.Apple> greenApples2 = filterApples(inventory, (FilteringApples1.Apple a) -> "green".equals(a.getColor()));
        System.out.println(greenApples2);

        // [Apple{color='green', weight=155}]
        List<FilteringApples1.Apple> heavyApples2 = filterApples(inventory, (FilteringApples1.Apple a) -> a.getWeight() > 150);
        System.out.println(heavyApples2);

        // []
        List<FilteringApples1.Apple> weirdApples = filterApples(inventory, (FilteringApples1.Apple a) -> a.getWeight() < 80 ||
                "brown".equals(a.getColor()));
        System.out.println(weirdApples);
    }


    public static List<FilteringApples1.Apple> filterApples(List<FilteringApples1.Apple> inventory, Predicate<FilteringApples1.Apple> p) {
        List<FilteringApples1.Apple> result = new ArrayList<>();
        for (FilteringApples1.Apple apple : inventory) {
            if (p.test(apple)) {
                result.add(apple);
            }
        }
        return result;
    }

    public static class Apple {
        private int weight = 0;
        private String color = "";

        public Apple(int weight, String color) {
            this.weight = weight;
            this.color = color;
        }

        public Integer getWeight() {
            return weight;
        }

        public void setWeight(Integer weight) {
            this.weight = weight;
        }

        public String getColor() {
            return color;
        }

        public void setColor(String color) {
            this.color = color;
        }

        public String toString() {
            return "Apple{" +
                    "color='" + color + '\'' +
                    ", weight=" + weight +
                    '}';
        }
    }

}

java8中内置filter函数

static <T> Collection<T> filter(Collection<T> c, Predicate<T> p);

这样你甚至都不需要写 filterApples 了,因为比如先前的调用

filterApples(inventory, (Apple a) -> a.getWeight() > 150 );

就可以直接调用库方法 filter :

filter(inventory, (Apple a) -> a.getWeight() > 150 );

以上所述就是小编给大家介绍的《java8-谓词(predicate)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

信号与噪声

信号与噪声

[美] 纳特•西尔弗 / 胡晓姣、张新、朱辰辰 / 中信出版社 / 2013-8 / 69.00元

【编辑推荐】 从海量的大数据中筛选出真正的信号, “黑天鹅”事件也可提前预知! “本书将成为未来十年内最重要的书籍之一。”——《纽约时报》 “对于每一个关心下一刻可能会发生什么的人来说,这都是本必读书。”——理查德•泰勒 《华尔街日报》2012年度10本最佳非虚构类图书之一 《经济学人》杂志2012年度书籍 亚马逊网站2012年度10本最佳非虚构类图书之一......一起来看看 《信号与噪声》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具