1 lamda函数概念
简单理解为匿名函数:
public int add(int x, int y) {
return x + y;
}
转成 lamda函数:(int x, int y) -> x + y;
或者(x, y) -> x + y;编译器可以自动识别参数类型
2 lamda类型
lamda表达式的类型,叫做“目标类型(target type)”。lamda表达式的目标类型是“函数接口(functional interface)”Java8才引入。
一个接口,如果只有一个显式声明的抽象方法,那么它就是一个函数接口。
一般用@FunctionalInterface标注出来(也可以不标注,毕竟只有一个函数/方法)
为什么只能定义一个函数接口呢: 因为我们的lamda函数都是通用型的方法,编译器要能自动识别是哪个方法。
示例:
//这个是线程接口
@FunctionalInterface
public interface Runnable { void run(); }
也可以申明多个接口,但其中一个接口必须是Object默认继承的(equals所有类接口都会继承Object),所以可以显示在lamda接口申明多个函数。
如下:
public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }
根据接口类型, 编译器自动匹配方法。包括自定义的lamda接口,如
@FunctionalInterface
public interface MyRunnable {
public void run();
}
下属写法都正确, 自动匹配对应的接口函数
Runnable r1 = () -> {System.out.println("Hello Lambda!");};
MyRunnable2 r2 = () -> {System.out.println("Hello Lambda!");};
这说明一个λ表达式可以有多个目标类型(函数接口),只要函数匹配成功即可。
3 lamda表达式使用
3.1 内部匿名类,各种回调,比如事件响应器、传入Thread类的Runnable等
Thread oldSchool = new Thread( new Runnable () {
@Override
public void run() {
System.out.println("This is from an anonymous class.");
}
} );
Thread gaoDuanDaQiShangDangCi = new Thread( () -> {
System.out.println("This is from an anonymous method (lambda exp).");
} );
第二个为lamda表达式, 编译器会自动推断出来:一个Thread的构造函数接受一个Runnable参数,而传入的λ表达式正好符合其run()函数,所以Java编译器推断它为Runnable
3.2 集合类批处理操作
3.2.1)list
集合类的批处理操作API的目的是实现集合类的“内部迭代”(以前式用户自己写for循环自己迭代),并期望充分利用现代多核CPU进行并行计算。
for(Object o: list) { // 外部迭代
System.out.println(o);
}
可以写成:
list.forEach(o -> {System.out.println(o);}); //forEach函数实现内部迭代
3.2.2) 流
List<Shape> shapes = ...
shapes.stream()
.filter(s -> s.getColor() == BLUE)
.forEach(s -> s.setColor(RED));
filter方法的参数是Predicate类型,forEach方法的参数是Consumer类型,它们都是函数接口,所以可以使用λ表达式
还有一个方法叫parallelStream(),顾名思义它和stream()一样,只不过指明要并行处理,以期充分利用现代CPU的多核特性。
下面是典型的大数据处理方法,Filter-Map-Reduce:
//给出一个String类型的数组,找出其中所有不重复的素数
public void distinctPrimary(String... numbers) {
List<String> l = Arrays.asList(numbers);
List<Integer> r = l.stream()
.map(e -> new Integer(e))
.filter(e -> Primes.isPrime(e))
.distinct()
.collect(Collectors.toList());
System.out.println("distinctPrimary result is: " + r);
}
你可能会觉得在这个例子里,List l被迭代了好多次,map,filter,distinct都分别是一次循环,效率会不好。实际并非如此。这些返回另一个Stream的方法都是“懒(lazy)”的,而最后返回最终结果的collect方法则是“急(eager)”的。在遇到eager方法之前,lazy的方法不会执行。
3.2.3) Optional使用
Optional本身用来解决 java臭名昭著的空指针问题,但是借助lamda表达式,做了一些很强大的功能。
1)map
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); } }
该函数的参数定义了一个lamda目标函数,当Optional值为空时,返回空。
不为空值,则将value作为lamda目标函数的参数T,返回值R根据参数实际返回
加入Opational调用如下方法:
map(StreamExecutionEnvironmentFactory::createExecutionEnvironment)
将根据实际值是否为空。 部位空则调用 lamda表达式的方法引用 StreamExecutionEnvironmentFactory.createExecutionEnvironment(value)
4 方法引用
任何一个λ表达式都可以代表某个函数接口的唯一方法的匿名描述符。我们也可以使用某个类的某个具体方法来代表这个描述符,叫做方法引用
/c1 与 c2 是一样的(静态方法引用)
Comparator<Integer> c2 = (x, y) -> Integer.compare(x, y);
Comparator<Integer> c1 = Integer::compare;
//下面两句是一样的(实例方法引用1)
persons.forEach(e -> System.out.println(e));
persons.forEach(System.out::println);
//下面两句是一样的(实例方法引用2)
persons.forEach(person -> person.eat());
persons.forEach(Person::eat);
//下面两句是一样的(构造器引用)
strList.stream().map(s -> new Integer(s));
strList.stream().map(Integer::new);
参考文档:https://blog.csdn.net/ioriogami/article/details/12782141