最近入职了滴滴,在看公司业务代码的时候发现代码中使用到了大量的stream流方式来简化代码,这对于没有接触过stream流的人来说看起来是十分费劲的,所以我打算写一个专题来讲解一下stream流的一些操作和代码书写!
目录
有关Stream流及函数式编程的相关代码均在我的码云上查看:徐明园/JavaCode - Gitee.com
并内附了详细的Stream流README文档:
一,函数式接口
1.1 前言
在了解Stream流之前我们需要先了解一下Lambda表达式,而Lambda表达式就是Java中函数式编程的体现,所以我们先来讲解一下什么是函数式接口(函数式接口就是可以适用于Lambda使用的接口)。
1.2 函数式接口
什么样的接口可以称作为函数式接口,一般认为接口中有且仅有一个抽象方法(默认方法和static方法除外)的接口称为函数式接口,具体格式如下:
修饰符 interface 接口名称{
public abstract 返回值类型 方法名称(可选参数信息); //public abstract可省略
//其他非抽象方法内容(其他方法不影响)
}
或者使用了@FunctionalInterface注解(该注解的作用就是检测一个接口是不是函数式接口),你可以简单的理解为这个注解起到了一个Mark的作用,只是为了告诉编译器此接口是否是函数式接口(功能类似于Serializable只是标记该类是否可以序列化),我们在查看源码的时候可以看到我们一些常用的接口,如Runnable,Comsumer等接口的源码上都加上了@FunctionalInterface注解:
同时我们自己可以定义函数式接口,主要需要注意的是接口中有且仅有一个抽象方法,为了保险起见,我们也可以加上@FunctionalInterface注解,如果接口内的方法满足函数式接口的规范则该注解会编译通过,否则会编译失败:
仅有一个抽象方法时加上@FunctionalInterface注解编译通过:
/**
* 有且仅有一个抽象方法method时加上注解编译通过
*/
@FunctionalInterface
interface MyInterface {
void method();
}
超过一个抽象方法时加上@FunctionalInterface注解编译失败:
/**
* 有两个抽象方法method method1时加上注解编译失败
*/
@FunctionalInterface
interface MyInterface {
void method();
void method1();
}
二,函数式接口的使用
函数式接口的使用按照大类可以分为两大类(细分参数个数的话种类更多,不做深究,希望读者可以举一反三):
2.1 函数式接口作为参数
public class FunctionalUseDemo1 {
//定义一个方法threadStart,参数使用函数式接口Runnable
public static void threadStart(Runnable runnable){
new Thread(runnable).start();
}
public static void main(String[] args) {
//调用threadStart方法,方法的参数是一个接口,可以传递这个接口的匿名内部类
threadStart(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开启线程");
}
});
//使用Lambda表达式优化
threadStart(()->System.out.println(Thread.currentThread().getName()+"开启线程"));
}
}
2.2 函数式接口作为返回值(返回值是一个函数式接口)
public class FuntionalUseDemo2{
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(9);
list.add(2);
list.add(5);
list.add(1);
System.out.println("排序前: " + list);
Collections.sort(list,getComparator());
System.out.println("---------------------");
System.out.println("排序后: " + list);
}
public static Comparator<Integer> getComparator() {
return new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
};
//lambda表达式优化
return (o1, o2) -> o1 - o2;
}
}
注意:所有函数式接口均可以使用Lambda表达式进行优化,优化的核心是只关注参数和方法体内的具体执行,方法名及其他不需要关注!
三,函数式接口结合Lambda表达式的练习
下面给出三个练习题,读者先将函数式接口转换成匿名内部类表达式,再通过匿名内部类表达式优化成Lambda表达式(记住转换成Lambda表达式时候只关注参数和方法体内具体执行):
3.1 练习一
import java.util.function.IntBinaryOperator;
public class LambdaDemo1 {
public static void main1(String[] args) {
// new Thread(new Runnable() {
// @Override
// public void run() {
// System.out.println("线程执行!");
// }
// }).start();
new Thread(() -> {
System.out.println("线程执行");
}).start();
}
public static void main(String[] args) {
IntBinaryOperator operator = new IntBinaryOperator() {
@Override
public int applyAsInt(int left, int right) {
return left + right;
}
};
int ret = calc(operator);
System.out.println(ret);
}
public static int calc(IntBinaryOperator operator) {
int a = 10;
int b = 20;
return operator.applyAsInt(a, b);
}
}
3.2 练习二
import java.util.function.IntBinaryOperator;
public class LambdaDemo2 {
public static void main1(String[] args) {
IntBinaryOperator operator = new IntBinaryOperator() {
@Override
public int applyAsInt(int left, int right) {
return left + right;
}
};
int ret = calc(operator);
System.out.println(ret);
}
public static void main2(String[] args) {
int ret = calc(new IntBinaryOperator() {
@Override
public int applyAsInt(int left, int right) {
return left + right;
}
});
System.out.println(ret);
}
public static void main(String[] args) {
int ret = calc((int left, int right) -> {
return left + right;
});
System.out.println(ret);
}
public static int calc(IntBinaryOperator operator) {
int a = 10;
int b = 20;
return operator.applyAsInt(a, b);
}
}
3.3 练习三
import java.util.function.IntPredicate;
public class LambdaDemo3 {
public static void main(String[] args) {
printNum(new IntPredicate() {
@Override
public boolean test(int value) {
return value % 2 == 1;
}
});
}
// public static void main1(String[] args) {
// printNum((int value) -> {
// return value % 2 == 1;
// });
//
// }
public static void printNum(IntPredicate predicate) {
int[] arr = {1, 2, 3, 4, 5};
for (int i : arr) {
if(predicate.test(i)) {
System.out.println(i);
}
}
}
}