一、Lambda表达式
什么是Lambda
Lambda表达式是JDK 8开始新增的一种语法形式;
作用:
用于简化匿名内部类的代码写法。
注意 : Lambda表达式只能简化函数式接口的匿名内部类!!!
什么是函数式接口?
有且仅有一个抽象方法的接口
注意:将来我们见到的大部分函数式接口,上面都可能会有一个@FunctionalInterface的注解,有该注解的接口就必定是函数式接口。
Lambda表达式的标准格式
组成Lambda表达式的三要素:
形参列表、箭头、代码块
(被重写方法的形参列表) -> {
被重写方法的方法体代码。
}
/*
目标:掌握Lambda表达式的格式
小结:
1.Lambda表达式的作用
(参数列表) -> {方法体}
2.Lambda表达式的组成格式
*/
public class Demo1 {
public static void main(String[] args) {
test(new A() {
@Override
public void show() {
System.out.println("匿名内部类的实现");
}
});
//Lambda表达式:简化匿名内部类
test( () ->{
System.out.println("匿名内部类的实现");
} );
}
//自定义方法
public static void test(A a){
a.show();
}
}
@FunctionalInterface //标记该接口是函数式接口(有且只有一个抽象方法),函数式接口使用时可以使用Lambda表达式替换
interface A{
void show();
}
Lambda表达式的省略规则
Lambda思想:可推导的就可省略,只保留方法要执行的核心代码
省略规则:
➢ 参数类型可以省略。
比如(Integer o1, Integer o2) 省略后 (o1, o2)
➢ 如果参数有且仅有一个,参数类型和小括号都可以省略。
比如(String s) 省略后 s
➢ 如果代码块的语句只有一条,可以省略大括号,分号和return
二、方法引用
方法引用是java8的新特性之一, 可以直接引用已有Java类或对象的方法或构造器。方法引用与
Lambda表达式结合使用,可以进一步简化代码。
方法引用格式
1.静态方法的引用: 类名::静态方法
使用场景:
如果某个Lambda表达式里只是调用一个静态方法,并且前后参数的形式一致,就可以使用静态方法引用。
2.实例方法的引用: 对象名::实例方法
使用场景:
如果某个Lambda表达式里只是调用一个实例方法,并且前后参数的形式一致,就可以使用实例方法引用。
3.特定类型的方法引用:类型::方法
使用场景:
如果某个Lambda表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数是作为方法的主调,后面的所有参数都是作为该实例方法的入参的,则此时就可以使用特定类型的方法引用。
4.构造器引用:类名::new
使用场景:
如果某个Lambda表达式里只是在创建对象,并且前后参数情况一致,就可以使用构造器引用。
方法引用前提
1. Lambda表达式中仅仅调用了一个方法。
2. 调用的方法与要实现的抽象方法的参数和返回值一致时,可以使用方法引用代替
/*
匿名内部类的抽象方法中仅仅调用了一个方法。
调用的方法与要实现的抽象方法的参数和返回值一致时,可以使用方法引用代替。
*/
public class Demo {
public static void main(String[] args) {
/* Date date = new Date();
printTime(new TimeUtil() {
@Override
public long showTime() {
return date.getTime(); //getTime方法去代替了抽象的showTime方法
}
});
//方法引用
//引用了date对象的getTime,去实现了接口的抽象方法(移花接木)
printTime(date::getTime);*/
printTime(new TimeUtil() {
@Override
public long showTime() {
return System.currentTimeMillis();
}
});
//方法引用代替showTime的功能(静态方法引用)
printTime(System::currentTimeMillis);
}
public static void printTime(TimeUtil tn) {
long time = tn.showTime();
System.out.println(time);
}
}
@FunctionalInterface
interface TimeUtil {
//获取毫秒值
long showTime();
}
三、Stream流
Stream流概述
也叫Stream流,是Jdk8开始新增的一套API (java.util.stream.*),可以用于操作集合或者数组的数据。Stream流相当于工厂里面的流水线,提供了丰富的API,方便对数据进行加工处理。
优势: Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操作集合或者数组中的数据,代码更简洁,可读性更好。
Stream流操作步骤
➢ 获取Stream流
创建一条流水线,并把数据放到流水线上准备进行操作。
➢ 中间方法
流水线上的操作,一次操作完毕之后,还可以继续进行其他操作。
➢ 终结方法
是流水线上的最后一个操作,一个Stream流只能有一次终结方法。
使用注意事项
1、创建Stream流
方式1:根据集合获取流 ——针对于集合
Collection根接口中提供了 stream( ) 方法可以获取流。
单列集合:直接调用stream()方法即可。
双列集合:先获取键和值的单列集合,再通过集合获取键和值各自的Stream流。
方式2:根据Arrays工具类方法获取流 ——针对于数组
Arrays类提供了静态的stream(T[] array)方法可以获取流。
方式3:根据of方法获取流——针对于其他数据类型
Stream类提供了静态的 of( ) 方法,可以创建一个流对象。
Stream.of(T ... values)
public class demo04 {
/*
目标:掌握获取Stream流的3种方式
小结:
方式1:根据集合对象获取流
单列集合: 集合对象.stream()
双列集合:
先获取键的集合,在通过键的集合获取流
方式2:根据Arrays工具类获取流
Arrays.stream(T[] array)
方式3:根据of方法获取流
static <T> Stream<T> of(T... values)
*/
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
Stream<Integer> stream = list.stream();
// Map集合
HashMap<Integer, Integer> map = new HashMap<>();
// 先单独获取键 或者 值的 集合
Set<Integer> set = map.keySet();
Stream<Integer> stream1 = set.stream();
// 值集合————使用Collection中的vlaus方法,获取map中的值集合。
Collection<Integer> values = map.values();
Stream<Integer> stream2 = values.stream();
// 针对于数组,字符串等
Integer[] arr = {11, 22, 33, 44};
Stream<Integer> stream3 = Arrays.stream(arr);
Stream<String> stream4 = Stream.of("abc", "defa");
}
}
2、常用方法
中间方法指的是调用完成后会返回新的stream流,可以继续使用【支持链式编程】
终结方法指的是调用完成后,不会返回新的Stream了,无法继续使用流了
注意:sorted()默认是升序的,使用自定义类型时要指定排序规则
public class demo02 {
public static void main(String[] args) {
// 需求:定义一个学生类【类中包含 姓名,成绩】,定义6个学生对象,添加到集合中
List<Student> list = new ArrayList<>();
Collections.addAll(list,
new Student("张三",100.0),
new Student("李四",66.6),
new Student("张三",100.0),
new Student("王五",88.0),
new Student("赵六",99.9)
);
// 实现下面的功能:实现学生的去重【如果学生对象名字和成绩相同,则认为是同一个人】,
// 把学生中成绩及格的同学找出,然后成绩按照升序排序, 跳过前2个学生,
list.stream().distinct().filter(student -> {
return student.getScore() >= 60.0;
}).sorted((s1,s2) -> {
return Double.compare(s1.getScore(), s2.getScore());
}).skip(2).forEach(System.out::println);
// 最后把符合条件的学生的名字找出,输出结果
}
}
public class Student {
//重写hashCode()和equals()
//认为学生对象名字和成绩相同,则认为是同一个
//配合distinct()使用
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Double.compare(student.score, score) == 0 && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, score);
}
}
3、注意事项
⚫ 一个Stream流对象只能操作一次。
⚫ 调用中间方法会返回新流,以便下次操作使用。
⚫ 终结方法如果没调用,中间方法也不会执行,表示该流还没有操作,可以拿到返回值。
4、流的收集
收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回。
Stream流:方便操作集合/数组的手段; 集合/数组:才是开发中的目的。
public class Demo {
/*
目标:能够把Stream对应转化 集合 / 数组
*/
public static void main(String[] args) {
// 定义6个学生对象
Student s1 = new Student("张三", 100);
Student s2 = new Student("李四", 48);
Student s3 = new Student("王五", 95);
Student s4 = new Student("赵六", 88);
Student s5 = new Student("田七", 90);
// 添加到集合中
ArrayList<Student> list = new ArrayList<>();
Collections.addAll(list, s1, s2, s3, s4, s5);
// 筛选出考试前三面的同学,收集成一个List
List<Student> list1 = list.stream()
.sorted((o1, o2) -> {
return Double.compare(o2.getScore(), o1.getScore());
})
.limit(3)
.collect(Collectors.toList());
System.out.println(list1);
System.out.println("-------------------------------");
// 筛选出考试前三面的同学,收集成一个Set
Set<Student> set = list.stream()
.sorted((o1, o2) -> {
return Double.compare(o2.getScore(), o1.getScore());
})
.limit(3)
.collect(Collectors.toSet());
System.out.println(set);
System.out.println("-------------------------------");
// 把学生的名字和成绩 组成一个Map
/* Map<String, Double> map = list.stream()
.collect(Collectors.toMap(new Function<Student, String>() {
@Override
public String apply(Student student) {
return student.getName();
}
},
new Function<Student, Double>() {
@Override
public Double apply(Student student) {
return student.getScore();
}
})
);
System.out.println(map);*/
Map<String, Double> map2 = list.stream()
.collect(Collectors.toMap(k -> k.getName(), v -> v.getScore()));
System.out.println(map2);
System.out.println("-------------------------------");
// 把stream收集为一个数组
Object[] array = list.stream().toArray();
System.out.println(Arrays.toString(array));
}
}
/*
已知两个集合数据:
第一个队伍数据:"令狐冲", "风清扬", "任我行", "东方不败", "岳不群" Stream.of()
第二个队伍数据:"马云", "李彦宏", "刘强东", "马化腾", "马保国" Stream.of()
要求:
第一个队伍只要名字为3个字的姓名。filter()
第一个队伍筛选之后只要前3人。limit()
第二个队伍只要姓马的姓名。startsWith("马")
第二个队伍不要前2人。skip()
将两个队伍合并到一个队伍 concat(流1,流2)
根据姓名创建Person对象, map()
存储到一个新集合。 collect()
遍历集合 forEach()
使用Stream流式编程完成
*/
public class Demo1 {
public static void main(String[] args) {
Stream<String> s1 = Stream.of("令狐冲", "风清扬", "任我行", "东方不败", "岳不群").filter(s -> s.length() == 3).limit(3);
Stream<String> s2 = Stream.of("马云", "李彦宏", "刘强东", "马化腾", "马保国").filter(s -> s.startsWith("马")).skip(2);
//将两个队伍合并到一个队伍,根据姓名创建Person对象
/* Stream.concat(s1,s2).map(new Function<String, Person>() {
@Override
public Person apply(String s) {
return new Person(s);
}
});*/
Stream.concat( s1, s2).map(Person::new).collect(Collectors.toList()).forEach(System.out::println);
//一行代码(不建议)
//Stream.concat( Stream.of("令狐冲", "风清扬", "任我行", "东方不败", "岳不群").filter(s -> s.length() == 3).limit(3), Stream.of("马云", "李彦宏", "刘强东", "马化腾", "马保国").filter(s -> s.startsWith("马")).skip(2)).map(Person::new).collect(Collectors.toList()).forEach(System.out::println);
}
}