第一个Lambda表达式 先来看一段根据字符串长度排序的Test.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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表达式取而代之:
1 2 3 4 5 6 7 8 9 10 11 12 13 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的布尔表达式时,就可以使用这个接口。直接上代码。
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 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的对象,并对它执行某些操作,就可以用这个借口。直接上代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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,将输入对象的信息映射到输出,可以使用这个接口,直接上代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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表达式变为方法引用就是
1 2 3 4 5 6 7 8 9 10 11 12 13 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可以这么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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); System.out.println(l); } }
复合Lambda表达式 许多函数式接口,比如用于传递lambda表达式的comparator,function,predicate都提供了允许你进行复合的方法,意味着你可以把多个简单的lambda表达式复合成复杂的表达式,函数式接口为什么有多个方法呢?这是因为使用的都是默认方法 。
比较器复合 java Arrays.sort(s1, Comparator.comparingInt(String::length))
展示了静态方法comparingInt根据提取用于比较的键值的Function来返回一个comparator。
逆序 如果我想对字符串长度逆序怎么办,接口有一个默认方法reversed可以使给定的比较器逆序。
1 2 3 4 5 6 7 8 9 10 11 12 13 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。
1 2 3 4 5 6 7 8 9 10 11 12 13 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方法即可。
1 2 3 4 5 6 7 8 9 10 11 12 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类:
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 32 33 34 35 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中的红苹果提取出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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方法即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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复合即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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的语义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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表达式,不要传入错的参数。