什么是 Lambda表达式?
对于 Java 中的 lambda 表达式,我们可以把它简单的理解为一个函数。
比如,我们需要这样一个函数,它需要把两个数字相加,将结果返回 则可以这样定义
但是我们知道,Java中函数是没有办法单独定义存在的,所以我们需要一个接口才能声明这个函数功能
1 2 3
| interface Function { int add(int a, int b); }
|
当我们需要实现这个方法的时候,一种方法就是定义一个新的类来实现这个接口
1 2 3 4 5 6
| class Calc implements Function { @Override public int add(int a, int b) { return a + b; } }
|
除此之外,还有一种方法就是使用匿名类
1 2 3 4 5 6
| Function addFunc = new Function() { @Override public int add(int a, int b) { return a + b; } };
|
而在 Java8 中,我们又有了一种更简单的写法,这也就是lambda表达式
1
| Function addFunc1 = (a, b) -> a + b;
|
当然,既然是为了表达一个函数所创建的接口,所以定义的接口中就必须只能有一个方法。对于这类接口,它还有一个特殊的名字: 函数式接口,我们可以在对应的接口上面添加@FunctionalInterface
注解来表明它的身份。
如何使用?
上一节我们可以知道,lambda就是函数式接口的实现,所以要想使用lambda表示式,我们必须先定义一个函数式接口,这样任何需要函数式接口的地方,我们都可以传递lambda表达式进去。我们也可以自己定义方法,接受lambda表达式参数
JDK中已经定义了很多常用的函数式接口,比如Comparator
接口
1 2 3 4
| @FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2); }
|
这样我们在排序的时候,接受 Comparator接口的地方就可以传递lambda表达式了,非常方便
1 2
| List<String> list = ...; list.sort((s1, s2) -> s1.compareTo(s2));
|
基本常用的函数式接口,JDK已经都帮我们内置好了,不需要自己定义,大致有以下几类
常用函数式接口
Predicate
谓词 - 用来对数据进行判断,返回true或false
1 2 3 4
| @FunctionalInterface public interface Predicate<T> { boolean test(T t); }
|
Function
用于转换数据,将前一个参数的类型转为后面的类型
1 2 3 4
| @FunctionalInterface public interface Function<T, R> { R apply(T t); }
|
Supplier
生产者,用来生产数据,无参数,返回一个对象
1 2 3 4
| @FunctionalInterface public interface Supplier<T> { T get(); }
|
Consumer
消费者,就是用来消费数据,接受一个数据,无返回值
1 2 3 4
| @FunctionalInterface public interface Consumer<T> { void accept(T t); }
|
Lambda表达式复合使用
函数式接口的功能都比较单一,有时候无法满足我们的需求,这时候就需要将多个lambda表达式组合到一起使用来完成功能
比较器复合
1 2 3 4 5 6 7 8
| Comparator<User> ageComparator = (u1, u2) -> u1.getAge().compareTo(u2.getAge()); Comparator<User> nameComparator = (u1, u2) -> u1.getAge().compareTo(u2.getAge());
Comparator<User> comparator = ageComparator.reversed();
comparator = ageComparator.thenComparing(nameComparator);
|
上面只是为了方便理解,实际比较时,可以使用 Comparator.comparing()
方法
如按照年龄排序: Comparator<User> newAgeComparator = Comparator.comparing(User::getAge);
谓词复合
谓词接口有三个方法,negate
, and
,or
来分别表示 非、与、或
1 2 3 4 5 6 7 8 9
| Predicate<User> userPred1 = u -> u.getAge() > 18;
Predicate<User> userPred2 = u -> u.getWeight() > 80;
Predicate<User> newPred = userPred1.and(userPred2);
Predicate<User> newPred2 = userPred1.or(userPred2);
|
注意:如果and
和or
在一起使用,则会按照书写顺序来定优先级
函数复合
Function
接口提供了两个方法andThen
和compose
用来组合函数
andThen用在执行完当然函数,将结果用户参数中的下一个函数
而 compose顺序则相反,是执行完参数中的函数,将结果用于当前调用函数
1 2 3 4 5 6 7 8 9 10 11 12
| Function<Integer, Integer> f = x -> x + 1; Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
System.out.println(h.apply(10));
Function<Integer, Integer> compose = f.compose(g);
System.out.println(compose.apply(10));
|
附:Java8其他
流的扁平化-flatMap
flatMap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流
1 2 3 4 5 6 7 8 9 10 11
| List<String> words = Arrays.asList("hello", "world"); List<String> result = words.stream() .map(word -> word.split("")) .flatMap(Arrays::stream) .distinct() .collect(Collectors.toList()); System.out.println(result);
|
1 2 3 4 5 6 7 8 9 10 11
| List<Integer> num1 = Arrays.asList(1, 2, 3); List<Integer> num2 = Arrays.asList(3, 4);
List<int[]> collect = num1.stream() .flatMap(n1 -> num2.stream().map(n2 -> new int[]{n1, n2})) .collect(Collectors.toList()); collect.stream() .map(Arrays::toString) .forEach(System.out::println);
|
查找和匹配
这里需要额外注意,在集合(流)为空的时候,allMatch和noneMatch会返回true, 但是anyMatch会返回false
至少一个匹配
boolean b = words.stream().anyMatch("hello"::equals);
匹配全部元素
1 2
| boolean b1 = words.stream().allMatch("hello"::equals); boolean b2 = words.stream().noneMatch("hello"::equals);
|
查找元素
1 2
| Optional<String> h1 = words.stream().filter("hello"::equals).findAny(); Optional<String> h2 = words.stream().filter("hello"::equals).findFirst();
|
收集器接口
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
|
interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
Function<A, R> finisher();
BinaryOperator<A> combiner();
Set<Characteristics> characteristics(); }
|
具体例子可参考 Collectors.toList
实现
参考资料:《Java8 实战》