lamdba,函数式接口(函数式编程) 是JDK8 的新特性,
java 的编程思想是一切都是对象,我们在写代码的时候也注重一个方法是有一个对象来完成的;
而函数式编程更加关注的是函数(方法)本身,关注是函数做了什么事情;
官方释义:
A lambda expression is like a method: it provides a list of formal parameters and a body - an expression or block - expressed in terms of those parameters.
lambda表达式就像一个方法:它提供了一个形式参数列表和一个用这些参数表示的主体(表达式或块)。
lamdba 是JDK8 提供的语法糖,对匿名的内部类写法 进行 简化. 是函数式编程思想的一个体现
格式: (参数列表)->{代码块} 其参数列表或者方法列表是推断出来的
下面就是一般的表达式的写法;
lambda表达式的求值产生函数接口的实例。Lambda表达式求值不会导致表达式正文的执行;相反,这可能在稍后调用函数接口的适当方法时发生。
//没有参数时的表达式 多行代码需要使用{{}}
() -> {} // No parameters; result is void 没有参数 没有返回值(或者是返回值是void)
() -> 42 // No parameters, expression body
() -> null // No parameters, expression body
() -> { return 42; } // No parameters, block body with return
() -> { System.gc(); } // No parameters, void block body
() -> { // Complex block body with returns
if (true) return 12;
else {
int result = 15;
for (int i = 1; i < 10; i++)
result *= i;
return result;
}
}
//一个参数的时候 使用()包起来无所谓
(int x) -> x+1 // Single declared-type parameter
(int x) -> { return x+1; } // Single declared-type parameter
(x) -> x+1 // Single inferred-type parameter
x -> x+1 // Parentheses optional for
// single inferred-type parameter
(String s) -> s.length() // Single declared-type parameter
(Thread t) -> { t.start(); } // Single declared-type parameter
s -> s.length() // Single inferred-type parameter
t -> { t.start(); } // Single inferred-type parameter
//要不全部声明参数类型 要不都不声明 声明部分在编译时会报错
(int x, int y) -> x+y // Multiple declared-type parameters
(x, y) -> x+y // Multiple inferred-type parameters
(x, int y) -> x+y // Illegal: can't mix inferred and declared types
(x, final y) -> x+y // Illegal: no modifiers with inferred types
案例一 :
// 会自动推导出类型是Runnable 方法体是run 方法
new Thread(() -> {
System.out.println("start");
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("start");
}
}).start();
//可以看到 Runnable 是一个函数式接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
lamdba 表达式的参数要不是声明好类型,要不是推断的类型,但是不能混用(多个参数,声明其中部分类型)
零参数的lambda表达式是显式类型
参数类型声明的时候注意:
单个表达式或块。与方法体一样,lambda体描述每当调用发生时将执行的代码;
//void 类型返回
() -> {}
() -> { System.out.println("done"); }
// 有返回值
() -> { return "done"; }
() -> { if (...) return 1; else return 0; }
lambda表达式中使用但未声明的任何局部变量、形式参数或异常参数必须声明为final或有效final,否则在尝试使用时发生编译时错误;
注: 什么是有效final ? 也叫efftive final ; 他没有final 显示的修饰,并且后面不会对它二次赋值(官方文档的描述是是不在赋值预算符的左侧,或者是递增递减的前缀和后缀[++ – ]); 并且已经初始化
如果 T 是一个函数接口类型;,并且该表达式与从 T 派生的地面目标类型的函数类型一致,则 lambda 表达式在赋值上下文、调用上下文或强制转换上下文中与目标类型 T 兼容。
目标类型由 T 推导如下:
如果 T 是通配符参数化的函数接口类型,并且 lambda 表达式是显式类型的,那么将按照描类型描述述断目标类型。
如果 T 是一个通配符参数化的函数接口类型,而 lambda 表达式是隐式类型的,那么 ground 目标类型是 t 的非通配符参量化;
函数式接口:任何接口,如果只包含唯一一个抽象方法(除了 Object 的 public 方法之外) ,那么它就是一个函数式接口。
//是
interface Runnable {
void run();
}
//不是
interface NonFunc {
boolean equals(Object obj); //Object 方法
}
//是的
interface Func extends NonFunc {
int compare(String o1, String o2); //唯一的 非object 的一个抽象方法
}
//所以类似 java.util.Comparator 是一个函数式接口
interface Comparator<T> {
boolean equals(Object obj); // Object 方法
int compare(T o1, T o2);
}
//两个接口 clone 是Object 的非 public 的
interface Foo {
int m();
Object clone();
}
如果有两个方法(FI5 ) ,
@FunctionalInterface
public interface FI2 {
Iterable m(Iterable<String> arg);
}
@FunctionalInterface
public interface FI3 {
Iterable<String> m(Iterable arg);
}
@FunctionalInterface // 也是一个函数式接口
interface FI5 extends FI2, FI3 { / /这个也是 FI2.m 的方法签名可以替代 Iterable<String>
}
注意:@FunctionalInterface 作用,用于编译器校验你的接口是不是函数式接口
对于函数式接口:
通过声明和实例化类(15.9)来创建接口实例的常规过程
还可以使用方法引用表达式和 lambda 表达式来创建函数接口实例。
public static void main(String[] args) {
//常规过程
FI fi = new FIDemo();
fi.fun1();
// lambda 表达式
fi = ()-> System.out.println("FIDemo2");
fi.fun1();
}
static class FIDemo implements FI{
@Override
public void fun1() {
System.out.println("FIDemo1");
}
}
public interface FI {
void fun1();
}
java.util.function 包下面的四大种类
//供给型 生产型 没有参数 有输出的
Supplier<Integer> sup = () -> {
return 100;
};
//消费型 有输入 没有输出的 消费型(消费输入)
Consumer<String> consumer = (x) -> {
System.out.println(" return void" + x);
};
//有输入也有输出的 函数型接口 (第一个泛型是输入,第二个泛型是输出) 计算转换类型
Function<String, Integer> function = (x)-> x.length();
//判断型接口
Predicate<String> predicate = (x)->{
boolean b = x.length() > 5;
return b;
};
JDK8 的Stream 使用函数式编程模式,可以使我们对集合的操作就像对流一样链式操作;简化和方便对 集合的操作;
流的操作可以氛围三大部分
//链表获取流
List<User> users = getUsers();
Stream<User> stream = users.stream();
//数组获取流
Integer[] arr = {1,3,5,7,9};
Stream<Integer> stream1 = Arrays.stream(arr);
//map 可以转entryset 转流
中间的操作是对流中数据操作,方法返回的还是Stream 意味着后面还是可以接着中间操作的函数
数据准备
public static List<User> getUsers() {
List<User> users = new ArrayList<>();
users.add(new User(1, "孙菲菲", 4, "杨浦区"));
users.add(new User(1, "孙菲菲", 4, "杨浦区"));
users.add(new User(2, "熊大", 5, "青青草原"));
users.add(new User(3, "熊二", 10, "青青草原"));
return users;
}
数据过滤
Stream<T> filter(Predicate<? super T> predicate);
public static void filter() {
List<User> users = getUsers();
users.stream()
.filter(x -> x.getId() > 1) //中间是匿名函数 Predicate(结合2.3) 的实现 是一个判断条件
.forEach(x -> System.out.println(x));
}
//输出
User(id=2, name=熊大, age=5, address=青青草原)
User(id=3, name=熊二, age=10, address=青青草原)
数据转换
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
public static void map() {
List<User> users = getUsers();
users.stream()
.map(x -> {
return "银河系,地球村 :" + x.getAddress();
}) //中间是匿名函数 Function(结合2.3) 的实现 y有输入和输出, 输入是User 对象 输出是 String
.forEach(x -> System.out.println(x));
}
//输出
银河系,地球村 :杨浦区
银河系,地球村 :杨浦区
银河系,地球村 :青青草原
银河系,地球村 :青青草原
数据去重
Stream<T> distinct();
public static void distinct() {
List<User> users = getUsers();
users.stream()
.distinct() //依赖于Object 的equals 方法来实现
.forEach(x -> System.out.println(x));
}
排序
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
public static void sorted() {
List<User> users = getUsers();
users.stream()
.sorted()
.forEach(x -> System.out.println(x));
}
//报错
Exception in thread "main" java.lang.ClassCastException: com.common.util.User cannot be cast to java.lang.Comparable
两个方法
方法1:User 实现 Comparable 接口
方法2:sorted(Comparator<? super T> comparator) 使用带参数的
方法二实现
public static void sorted() {
List<User> users = getUsers();
users.stream()
.sorted(Comparator.comparingInt(User::getAge)) //sorted() 使用带参数的
.forEach(x -> System.out.println(x));
}
截取前面几条数据
Stream<T> limit(long maxSize);
public static void limit() {
List<User> users = getUsers();
users.stream()
.limit(3)
.forEach(x -> System.out.println(x));
}
//输出
User(id=1, name=孙菲菲, age=4, address=杨浦区)
User(id=1, name=孙菲菲, age=4, address=杨浦区)
User(id=2, name=熊大, age=5, address=青青草原)
跳过前面几条数据
Stream<T> skip(long n);
public static void skip() {
List<User> users = getUsers();
users.stream()
.skip(2)
.forEach(x -> System.out.println(x));
}
User(id=2, name=熊大, age=5, address=青青草原)
User(id=3, name=熊二, age=10, address=青青草原)
map 对象是一对一转出 , flatMap 可以一对多转出对象
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
public static void flatMap() {
List<User> users = getUsers();
users.stream()
.flatMap((Function<User, Stream<?>>) user -> Arrays.asList(user.getId(), user.getAddress()).stream()) //flatMap 内是一个Function ,Function 的输出是一个 子流(可以这样理解,每个元素拆分的流)
.forEach(x -> System.out.println(x));
}
//输出
1
杨浦区
1
杨浦区
2
青青草原
3
青青草原
遍历流中的数据
public static void foreach() {
List<User> users = getUsers();
users.stream()
.forEach(x -> System.out.println(x));
}
//输出
User(id=1, name=孙菲菲, age=4, address=杨浦区)
User(id=1, name=孙菲菲, age=4, address=杨浦区)
User(id=2, name=熊大, age=5, address=青青草原)
User(id=3, name=熊二, age=10, address=青青草原)
计算流中的数据
public static void count() {
List<User> users = getUsers();
long count = users.stream()
.filter(x -> x.getId() > 1)
.count();
System.out.println(count);
}
//输出
2
计算流中最大值,最小值
public static void max() {
List<User> users = getUsers();
Optional<User> max = users.stream()
.filter(x -> x.getId() > 1)
.max(Comparator.comparingInt(User::getAge));//.min(Comparator.comparingInt(User::getAge));
System.out.println(max.get()); //getAge 最大的
}
User(id=3, name=熊二, age=10, address=青青草原)
流中的元素转变成集合
public static void collect() {
List<User> users = getUsers();
List<Integer> list = users.stream()
.map(x -> x.getId())
.collect(Collectors.toList());
System.out.println(list);
System.out.println("=========");
Set<Integer> set = users.stream()
.map(x -> x.getId())
.collect(Collectors.toSet());
System.out.println(set);
System.out.println("=========");
Map<String, User> nameToUser = users.stream()
.collect(Collectors.toMap(User::getName, Function.identity()));
System.out.println(nameToUser);
System.out.println("=========");
//这里会报错 Exception in thread "main" java.lang.IllegalStateException: Duplicate key User(id=1, name=孙菲菲, age=4, address=杨浦区)
//需要先去重 或者下面方法 重复使用哪一个
//(x, x1) -> x 使用前一个
//(x, x1) -> x1 使用后一个
Map<String, User> nameToUser = users.stream()
.collect(Collectors.toMap(User::getName, Function.identity(), (x, x1) -> x1));
System.out.println(nameToUser);
System.out.println("=========");
Map<String, List<User>> nameToUserList = users.stream()
.collect(Collectors.groupingBy(User::getName));
System.out.println(nameToUserList);
System.out.println("=========");
}
public static void matchAndSelect() {
List<User> users = getUsers();
boolean b = users.stream().map(x -> x.getAge()).allMatch(x -> x > 10);
System.out.println("所有age>10:" + b);
b = users.stream().map(x -> x.getAge()).anyMatch(x -> x > 5);
System.out.println("任意age>5:" + b);
b = users.stream().map(x -> x.getAge()).noneMatch(x -> x > 10);
System.out.println("没有一个age > 10:" + b);
}
//元素
users.add(new User(1, "孙菲菲", 4, "杨浦区"));
users.add(new User(1, "孙菲菲", 4, "杨浦区"));
users.add(new User(2, "熊大", 5, "青青草原"));
users.add(new User(3, "熊二", 10, "青青草原"));
所有age>10:false
任意age>5:true
所有都<=10:true
public static void select() {
List<User> users = getUsers();
Optional<User> any = users.stream().findAny();
System.out.println(any);
Optional<User> first = users.stream().findFirst();
System.out.println(first);
}
//输出
Optional[User(id=1, name=孙菲菲, age=4, address=杨浦区)]
Optional[User(id=1, name=孙菲菲, age=4, address=杨浦区)]
对流中数据进行指定计算方式运行出结果( 聚合,缩减)
比如说求和
public static void reduce() {
List<User> users = getUsers();
Integer reduce = users.stream()
.map(x -> x.getAge())
.reduce(0, (integer, integer2) -> integer + integer2);
System.out.println(reduce);
}
大量数据的时候,可以使用并行流处理数据.
并行流的底层是把任务分配给多个线程(ForkJoinn Pool 线程池)去执行.
public static void parallelStream() {
List<User> users = getUsers();
Integer reduce = users.parallelStream()
.peek(x->{
System.out.println(Thread.currentThread().getName());
})
.map(x -> x.getAge())
.reduce(0, (integer, integer2) -> integer + integer2);
System.out.println(reduce);
}
//输出
ForkJoinPool.commonPool-worker-9
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-11
main
23