lambda表达式
lambda表达式含义: Lambda 表达式(函数式编程)”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型, JDK8开始引入语法
这里我们拿个创建线程的例子给大家看清楚到底什么是lambda表达式:
普通创建
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0;i<10;i++){
System.out.println(i);
}
}
});
lambda表达式写法
Thread t2 = new Thread(()->{
for (int i = 0;i<10;i++){
System.out.println(i);
}
});
使用前提
- 1.匿名内部类实现接口
- 2.这个接口是函数式接口: 接口中只有一个抽象方法(可以包含默认方法和静态方法)我们可以给一个符合函数式接口添加@FunctionalInterface注解,这样就显式的指明该接口是一个函数式接口,如果不是,编译器会直接提示错误。
- 3.作为方法参数使用
语法: (参数) -> { 要实现的那个方法的方法体 }
进一步简化方式:
- 如果方法有参数,参数类型可以省略
- 如果方法体只有一句代码, return关键字、;、{} 可以省略(必须一起省略)
- 如果方法中只有一个参数, () 可以省略
常用的函数式接口
1.Supplier接⼝
java.util.function.Supplier 接⼝仅包含⼀个⽆参的⽅法: T get() 。⽤来获取⼀个泛型参数指定类型的对象数据。由于这是⼀个函数式接⼝,这也就意味着对应的Lambda表达式需要“对外提供”⼀个符合泛型类型的对象数据。
public static Integer max01(Supplier<Integer> supplier){
return supplier.get();
}
public static void main(String[] args) {
Integer[] arr={12,15,180,99,102};
Integer integer = max01(() -> {
Integer max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
});
System.out.println(integer);
}
//获取一个数组中的最大值
2.Consumer接⼝
java.util.function.Consumer 接⼝则正好与Supplier接⼝相反,它不是⽣产⼀个数据,⽽是消费⼀个数据,其数据类型由泛型决定,抽象方法:accept。
public static void print(Consumer<person> consumer01,Consumer<person> consumer02,person p){
consumer01.andThen(consumer02).accept(p);
}
public static void main(String[] args) {
person p = new person("张无忌", 18);
print(T-> System.out.println(T.getName()+"是某某某的父亲"),T-> System.out.println(T.getName()+"今年"+T.getAge()+"岁"),p);
}
3.Predicate接⼝
有时候我们需要对某种类型的数据进⾏判断,从⽽得到⼀个boolean值结果。这时可以使 java.util.function.Predicate 接⼝,抽象方法:test。
//挑选数组中名字张开头和三个字组成的名字
public static void print(Predicate<person> predicate01,Predicate<person> predicate02,person[] p){
for (person person : p){
if(predicate01.and(predicate02).test(person)){
System.out.println(person);
}}
}
public static void main(String[] args) {
person[] ps = {new person("张三丰",23),
new person("张无忌",99),
new person("沈腾",45),
new person("张翠花",50),new person("张三",50)};
print(t->{return t.getName().startsWith("张");},t->{return t.getName().length()==3;},ps);
}
4.Function接⼝java.util.function.Function<T,R> 接⼝⽤来根据⼀个类型的数据得到另⼀个类型的数据(俗称为转换类型方法),前者称为前置条件,后者称为后置条件,抽象方法:apply。
//把String类型转换为Date类型
public static Date transfer(Function<String, Date> function, String str){
return function.apply(str);
}
public static void main(String[] args) {
Date transfer = transfer((t) -> {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return sdf.parse(t);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}, "2022-7-19 11:53:49");
System.out.println(transfer);
}
流
流的优点: 可以以更好的方式遍历数组或者集合,很大程度上简化lambda表达式代码。
Stream(流)是⼀个来⾃数据源的元素队列
- 元素是特定类型的对象,形成⼀个队列。 Java中的Stream并不会存储元素,⽽是按需计算。
- 数据源流的来源。可以是集合,数组等。
流的特征
- 流水线:中间操作都会返回流对象本身。这样多个操作可以串联成⼀个管道, 如同流式⻛格。这样做可以对操作进⾏优化,⽐如延迟执⾏和短路。
- 内部迭代:以前对集合遍历都是通过Iterator或者增强for的⽅式,显式的在集合外部进⾏迭代,这叫做外部迭代。Stream提供了内部迭代的⽅式,流可以直接调⽤遍历⽅法。
备注:“Stream流”其实是⼀个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值)。
使用流的步骤: 获取⼀个数据源→ 数据转换 → 执⾏操作获取想要的结果
获取⼀个数据源(获取流)
-
所有的 Collection 集合都可以通过 stream 默认⽅法获取流;
List<String> list = List.of("张三丰", "杨洋", "迪丽热巴"); Stream<String> stream1=list.stream(); Set<String> set =Set.of("手机","电脑","电视"); Stream<String> stream2=set.stream(); Map<String,Integer> map = Map.of("被子",23,"笔",5); Stream<String> stream3=map.keySet().stream(); Stream<Integer> stream4=map.values().stream(); Stream<Map.Entry<String,Integer>> stream5=map.entrySet().stream();
-
Stream 接⼝的静态⽅法 of 可以获取数组对应的流。
Stream<String> stream7 =Stream.of("张三丰", "杨洋", "迪丽热巴");
备注: of ⽅法的参数其实是⼀个可变参数,所以⽀持数组
String[] atrarry={"手机","电脑","电视"};
Stream<String> stream6 = Arrays.stream(atrarry);
常用方法
延迟⽅法:返回值类型仍然是 Stream 接⼝⾃身类型的⽅法,因此⽀持链式调⽤。(除了终结⽅法外,其余⽅法均为延迟⽅法。)
终结⽅法:返回值类型不再是 Stream 接⼝⾃身类型的⽅法,因此不再⽀持类似 StringBuilder 那样的链式调⽤。本⼩节中,终结⽅法包括 count 和 forEach ⽅法。
逐⼀处理: forEach
//逐一打印所有名字
public class Demo12StreamForEach {
public static void main(String[] args) {
Stream<String> stream = Stream.of("张⽆忌", "张三丰", "周芷若");
stream.forEach(name-> System.out.println(name));
}
过滤: filter:可以通过 filter ⽅法将⼀个流转换成另⼀个⼦集流。
//挑选张开头的姓名
public class Demo07StreamFilter {
public static void main(String[] args) {
Stream<String> original = Stream.of("张⽆忌", "张三丰", "周芷若");
Stream<String> result = original.filter(s -> s.startsWith("张"));
}
映射: map:如果需要将流中的元素映射到另⼀个流中,可以使⽤ map ⽅法。
public class Demo08StreamMap {
public static void main(String[] args) {
Stream<String> original = Stream.of("10", "12", "18");
Stream<Integer> result = original.map(str->Integer.parseInt(str));
}
统计个数: count:正如旧集合 Collection 当中的 size ⽅法⼀样,流提供 count ⽅法来数⼀数其中的元素个数
public class Demo09StreamCount {
public static void main(String[] args) {
Stream<String> original = Stream.of("张⽆忌", "张三丰", "周芷若");
Stream<String> result = original.filter(s -> s.startsWith("张"));
System.out.println(result.count()); // 2
}
取⽤前⼏个: limit:limit ⽅法可以对流进⾏截取,只取⽤前n个。
public class Demo10StreamLimit {
public static void main(String[] args) {
Stream<String> original = Stream.of("张⽆忌", "张三丰", "周芷若");
Stream<String> result = original.limit(2);
System.out.println(result.count()); // 2
}
跳过前⼏个: skip:如果希望跳过前⼏个元素,可以使⽤ skip ⽅法获取⼀个截取之后的新流
public static void main(String[] args) {
Stream<String> original = Stream.of("张⽆忌", "张三丰", "周芷若");
Stream<String> result = original.skip(2);
System.out.println(result.count()); // 1
}
组合: concat:如果有两个流,希望合并成为⼀个流,那么可以使⽤ Stream 接⼝的静态⽅法 concat
public class Demo12StreamConcat {
public static void main(String[] args) {
Stream<String> streamA = Stream.of("张⽆忌");
Stream<String> streamB = Stream.of("张翠⼭");
Stream<String> result = Stream.concat(streamA, streamB);
}
}