Java lambda表达式学习笔记

第一个Lambda表达式

先来看一段根据字符串长度排序的Test.java

package com.xzc;

import java.util.Arrays;
import java.util.Comparator;

public class Test {
    public static void main(String[] args) {
        String[] s1 ={"ab","a","abcd","abc"};
        Arrays.sort(s1, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });
        System.out.println(Arrays.toString(s1));        
    }
}

输出结果

[a, ab, abc, abcd]

如果我们用lambda表达式取而代之:

package com.xzc;

import java.util.Arrays;
import java.util.Comparator;

public class Test {
    public static void main(String[] args) {
        String[] s1 ={"ab","a","abcd","abc"};
        Arrays.sort(s1,(String o1, String o2) -> o1.length() - o2.length());
        System.out.println(Arrays.toString(s1));
    }

}

输出结果

[a, ab, abc, abcd]

可以发现简单许多,而其中的Arrays.sort(s1,(String o1, String o2) -> o1.length() - o2.length());就是我们的第一个lambda表达式。

常用的函数式接口

Predicate

java.util.function.Predicate<T>定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。在你需要涉及类型T的布尔表达式时,就可以使用这个接口。直接上代码。

package com.xzc;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class Test {
    public static <T> List<T> filter(List<T> list, Predicate<T> p){
        List<T> results = new ArrayList<T>();
        for(T l : list){
            if(p.test(l)){
                results.add(l);
            }
        }
        return results;
    }
    public static void main(String[] args) {
        List<String> l = new ArrayList<>();
        l.add("abcd");
        l.add("");
        l.add("abc");
        System.out.println(l);
        List<String> l1 = filter(l,(String s) -> (!s.isEmpty()));
        System.out.println(l1);
    }
}

输出结果:

[abcd, , abc]
[abcd, abc]

我们这里用泛型方法传入String类的List,对其中进行非空字符串判断,然后返回一个String类的List,其中剔除了空字符串。这里我们就是把进行判断的boolean函数进行参数化,然后在执行的时候将行为(这里是判断字符串非空)当做参数传入,我们这里的lambda表达式覆写了test方法。

Consumer

java.util.function.Consumer<T>定义了一个名为accept的抽象方法,它接受泛型T的对象,没有返回。如果你要访问T的对象,并对它执行某些操作,就可以用这个借口。直接上代码。

package com.xzc;


import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;


public class Test {
    public static <T> void forEach(List<T> list, Consumer<T> c){
        for (T l : list){
            c.accept(l);
        }
    }
    public static void main(String[] args) {
        forEach(Arrays.asList(1,2,3,4,5),(Integer integer) -> System.out.println(integer));
    }

}

输出结果

1
2
3
4
5

我们这里就是把参数化的行为accept,用lambda表达式进行覆写,覆写为打印list的每个对象。

Function

java.util.function.Function<T,R>接口定义了一个叫做apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象,如果你需要定义一个lambda,将输入对象的信息映射到输出,可以使用这个接口,直接上代码。

package com.xzc;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;


public class Test {
    public static <T,R> List<R> map(List<T> list, Function<T,R> f){
        List<R> result = new ArrayList<>();
        for(T l : list){
            result.add(f.apply(l));
        }
        return result;
    }
    public static void main(String[] args) {
        List<Integer> l = map(Arrays.asList("lambda","in","actions"),(String s)->s.length());
        System.out.println(l);
    }
}

输出结果:

[6, 2, 7]

这里我们对泛型T返回泛型R传入了String返回Integer,即覆写apply方法为传入String类s返回s的长度,即将String类输入,映射到一个Integer类输出,并且在map方法里把它添加到一个Integer类的List来执行这个apply行为。

方法引用

方法引用让你可以重复使用现有的方法定义,并像lambda一样传递它们。实际上某些情况下虽然方法引用简洁,但是不如lambda表达式可读性高。先举一个例子,我们将前面第一个lambda表达式变为方法引用就是

package com.xzc;

import java.util.Arrays;
import java.util.Comparator;

public class Test {
    public static void main(String[] args) {
        String[] s1 ={"ab","a","abcd","abc"};
        Arrays.sort(s1, Comparator.comparingInt(String::length));
        System.out.println(Arrays.toString(s1));
    }

}

java Comparator.comparingInt(String::length)这里就是方法引用,其中的方法不能带括号
方法引用可以被看做仅仅调用特定方法的lambda的一种快捷写法,如果一个lambda代表的只是数值解调用这个方法,那么最好是用名称去调用它,而不是描述如何调用它。
方法引用主要有三类:
1.指向静态方法的方法引用,例如Integer的parseInt方法,写作Integer::ParseInt
2.指向任意类型实例方法的方法引用,例如String的length方法,写作String::length
3.指向现有对象的实例方法的方法引用,假设你有一个局部变量a,用于存放A类型的对象,它支持实例方法m,那么你就可以写a::m。
第二种方法引用的思想就是你在引用一个对象的方法,而这个对象本身是lambda的一个参数,比如lambda表达式(String s) -> s.toUpperCase()可以写作String::toUpperCase,但第三种方法引用指的是,你在lambda中调用一个已经存在的外部对象中的方法,例如lambda表达式() -> expensiveTransaction.getValue()可以写作expensiveTransaction::getValue这里的expensiveTransaction是一个外部对象,不是lambda的一个参数。
现在,我们的Function可以这么写:

package com.xzc;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;


public class Test {
    public static <T,R> List<R> map(List<T> list, Function<T,R> f){
        List<R> result = new ArrayList<>();
        for(T l : list){
            result.add(f.apply(l));
        }
        return result;
    }
    public static void main(String[] args) {
        List<Integer> l = map(Arrays.asList("lambda","in","actions"), String::length);//方法引用 之前是(String s)->s.length()
        System.out.println(l);
    }
}

复合Lambda表达式

许多函数式接口,比如用于传递lambda表达式的comparator,function,predicate都提供了允许你进行复合的方法,意味着你可以把多个简单的lambda表达式复合成复杂的表达式,函数式接口为什么有多个方法呢?这是因为使用的都是默认方法

比较器复合

java Arrays.sort(s1, Comparator.comparingInt(String::length))展示了静态方法comparingInt根据提取用于比较的键值的Function来返回一个comparator。

逆序

如果我想对字符串长度逆序怎么办,接口有一个默认方法reversed可以使给定的比较器逆序。

package com.xzc;

import java.util.Arrays;
import java.util.Comparator;

public class Test {
    public static void main(String[] args) {
        String[] s1 ={"ab","a","abcd","abc"};
        Arrays.sort(s1, Comparator.comparingInt(String::length).reversed());
        System.out.println(Arrays.toString(s1));
    }

}

输出结果

[abcd, abc, ab, a]

比较器链

如果有两个字符串长度一样,哪个排在前面呢?可以来第二个比较,有一个默认方法是thenComparing。

package com.xzc;

import java.util.Arrays;
import java.util.Comparator;

public class Test {
    public static void main(String[] args) {
        String[] s1 ={"ab","a","abd","abc"};
        Arrays.sort(s1, Comparator.comparingInt(String::length).reversed());
        System.out.println(Arrays.toString(s1));
    }

}

这里我们输出结果是

[abd, abc, ab, a]

如果我们想先按长度逆序排序,再按字典顺序排序呢,也就是abc要在abd前面该怎么办,直接用thenComparing方法即可。

package com.xzc;

import java.util.Arrays;
import java.util.Comparator;

public class Test {
    public static void main(String[] args) {
        String[] s1 ={"ab","a","abd","abc"};
        Arrays.sort(s1, Comparator.comparingInt(String::length).reversed().thenComparing(String::toString));
        System.out.println(Arrays.toString(s1));
    }
}

输出结果

[abc, abd, ab, a]

谓词复合

谓词接口包括三个方法:negate,and和or,让你可以重用已有的predicate来创建更复杂的谓词。
我们先新建一个Apple类:

package com.xzc;

public class Apple {
    private int weight;
    private String color;
    public Apple(){

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

    public int getWeight() {
        return weight;
    }
    public void setColor(String color){
        this.color = color;
    }
    public String getColor(){
        return this.color;
    }

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

我们要把Apple类的List中的红苹果提取出来:

package com.xzc;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.function.Predicate;

public class Test {
    public static <T> List<T> getRedApple(List<T> list, Predicate<T> p){
        List<T> results = new ArrayList<>();
        for (T l : list){
            if (p.test(l)){
                results.add(l);
            }
        }
        return results;
    }
    public static void main(String[] args) {
        System.out.println(getRedApple(Arrays.asList(new Apple(150,"red"),new Apple(160,"green"),new Apple(160,"red")),(Apple a) -> (a.getColor().equals("red")||a.getColor().equals("Red"))));
    }


}

输出结果:

[Apple{weight=150, color=‘red’}, Apple{weight=160, color=‘red’}]

可以看到我们这里的predicate的test方法传入了我们的lambda表达式,判断苹果是红的。
如果我们想进一步,要重量大于150呢?用and方法即可。

package com.xzc;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.function.Predicate;

public class Test {
    public static <T> List<T> getRedApple(List<T> list, Predicate<T> p){
        List<T> results = new ArrayList<>();
        for (T l : list){
            if (p.test(l)){
                results.add(l);
            }
        }
        return results;
    }
    public static void main(String[] args) {
        Predicate<Apple> p = (Apple a) -> (a.getColor().equals("red")||a.getColor().equals("Red"));
        System.out.println(getRedApple(Arrays.asList(new Apple(150,"red"),new Apple(160,"green"),new Apple(160,"red")),p.and(a -> a.getWeight() > 150)));
    }


}

输出结果

[Apple{weight=160, color=‘red’}]

这里我们把p先单独拿出来,方便观察,注意

java p.and(a -> a.getWeight() > 150)
没有声明a是Apple类型,这里lambda表达式进行了类型检查。我们对p进行了and复合,要求重量在150之上,如果我们要不是红苹果的呢?用negate复合即可。

package com.xzc;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.function.Predicate;

public class Test {
    public static <T> List<T> getRedApple(List<T> list, Predicate<T> p){
        List<T> results = new ArrayList<>();
        for (T l : list){
            if (p.test(l)){
                results.add(l);
            }
        }
        return results;
    }
    public static void main(String[] args) {
        Predicate<Apple> p = (Apple a) -> (a.getColor().equals("red")||a.getColor().equals("Red"));
        System.out.println(getRedApple(Arrays.asList(new Apple(150,"red"),new Apple(160,"green"),new Apple(160,"red")),p.negate()));
    }


}

输出结果:

[Apple{weight=160, color=‘green’}]

可以看到筛出了不是红苹果的。

函数复合

最后,还可以把Function接口所代表的lambda表达式复合起来。接口有andThen和compose两个默认方法,都会返回一个Function实例。
andThen方法会返回一个函数,它先对输入应用一个给定函数,再对输出应用另一个函数。
但是注意f.andThen(g)代表着g(f(x)),即先执行f,再执行g,也符合andThen语义,f.compose(g)则是代表着f(g(x)),即f里复合g,也符合compose的语义。

package com.xzc;


import java.util.function.Function;

public class Test {

    public static void main(String[] args) {
        Function<Integer,Integer> f = x -> x + 1;
        Function<Integer,Integer> g = x -> 2 * x;
        Function<Integer,Integer> h = f.andThen(g);
        Function<Integer,Integer> y = f.compose(g);
        System.out.println(h.apply(1));
        System.out.println(y.apply(1));
    }


}

输出结果

4
3

这里h就是先把1加1,得到2,再乘2,得到4.而y呢是先执行g,先把1乘2得到2,再执行f,加1得到3。数学函数如此,可以类比把function泛型的各种类型输入得到的输出再做映射,比如我们得到了字符串长度,再+1。

package com.xzc;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;


public class Test {
    public static <T,R> List<R> map(List<T> list, Function<T,R> f){
        List<R> result = new ArrayList<>();
        for(T l : list){
            result.add(f.apply(l));
        }
        return result;
    }
    public static void main(String[] args) {
        Function<String,Integer> f = String::length;
        List<Integer> l = map(Arrays.asList("lambda","in","actions"), f.andThen(x -> x+1));
        System.out.println(l);
    }
}

输出结果

[7, 3, 8]

之前没有复合的结果是

[6, 2, 7]

可以发现,进行函数复合可以加快编程效率,就是注意复合是复合lambda表达式,不要传入错的参数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值