概述
在之前学习过 lambda表达式,其中在创建一个线程的时候使用了如下语法。
new Thread(() -> {
//代码块
}).start();
这样就成功了创建了一个线程,可在但是并没有因此而深入了解为什么要这样写,lambda表达式的具体用法到底是什么。今天便学习到了。
首先为了回答以上的问题需要先从函数式接口来入手。
函数式接口
简单的来说,函数式接口就是只包含一个抽象方法的接口,但一般会拥有多个默认方法。那么为什么要设计函数式接口呢,其实主要目的是将方法当做参数进行传递。
为了实现这样一个目的,所以在Java8中设计了lambda。
创建
一般的,我们创建一个函数式接口都要添加 @FunctionalInterface 注解。当然不加也没事,不过为了规范最好添加。
@FunctionalInterface
public interface Person {
void play(String msg);
}
实现
以前我们在集成一个接口的时候一般都是重新创建一个类,使用 implements关键字 来实现这个接口。而当我们这次采用lambda则不需要。
Person person = (msg) -> {
System.out.println(msg);
};
person.play("111");
当我们声明这个参数类型为 "Person" 的时候,就已经代表我们后续的lambda其实就是去实现这个类,而括号中代表的是对应的参数。花括号内代表具体代码。
值得记录的是,有时可以省略以下部分代码:
- 代码只有一句的时候可以省略花括号
- 代码只有一句的时候如果有返回值可以省略return
所以前面那句代码可以省略为:
Person person = () -> System.out.println(233);
写到这里应该已经发现了,我们已经成功的将这样一个接口实现为一个变量了,那么我们再回到看一眼 Thread 的代码。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
可以看到它其实是需要一个 Runnable 类型的参数,那么是不是代表这个参数类型其实也就是一个函数式接口呢?
@FunctionalInterface
public interface Runnable {
很明显,它也是这样的接口。自此,就解决了之前的疑问。
Stream流
为什么这篇博文要将这两者放在一起呢?其实是因为在Stream之中大量的用的了函数式接口来对一组元素进行操作。
使用
以下代码便可以获得一个 Stream 。
List<Integer> list = Arrays.asList(50, 120, 1, 74, 10, 5, 6);
Stream<Integer> stream = list.stream();
操作总的来说分为两种,中间操作和终止操作。简单的来说,中间操作就是对这组数据进行修改、删除...等等。而终止操作则是获得值、规约值..等等。
说到这里应该就理解了吧,其实Stream就是通过它实现了很多方便的功能,让我们可以少写很多冗余代码。
首先我们尝试使用 foreach 来进行遍历这个list,可以看到参数类型为 Consumer,就是消费者的意思,它就是一个函数式接口,而在该接口下拥有一个名为 accept 的抽象方法,并且拥有一个泛型参数,寓意为消费这个值。
所以,我们直接直接使用前面所学的实现接口方法。
list.stream()
.forEach((item) -> {
System.out.println(item);
});
这样,我们就可以成功将其遍历出来。
并行流
很明显,我们前面使用的方式简单的来说就是按照一定顺序来进行处理,也就是顺序流。但当我们拥有大量数据处理的时候速度必然会很慢,所以Java在它的基础上还添加了并行流。
其实就是加入了多线程概念。使用以下方法就可以获得并行流。
list.parallelStream()
当我们重新使用这样的方法执行 foreach 的时候,顺序并非前者那样,所以确信其本质就是多线程的流~
其他
其他用法都大同小异,除了消费者接口以外Java还集成了:
- Function 转换,将一个或多个值处理成某个结果
- Predicate 断言,判断当前值是否成立
- Comparator 排序,顾名思义,在之前的排序博文中就有用到这个接口。
- ...
本页的评论功能已关闭