Lambda函数
JDK8新增的语法形式, 使用Lambda函数替代某些匿名内部类对象,从而让程序代码更简洁,可读性更好。
基本使用
lambda表达式只能简化函数式接口的匿名内部类写法
// 1.定义抽象类
abstract class Animal {
public abstract void crt();
}
public class LambdaDome {
public staitc void main(String[] args) {
// 2.使用匿名内部类, 创建抽象类的子类对象
Animal a = new Animal() {
@Override
public void cry() {
sout("猫喵喵的叫")
}
}
// 3. 错误示范, 代码报错
// lambda表达式并不能简写所有匿名内部类的写法
Animal a = () -> {
sout("猫喵喵的叫")
}
a.cry(); // 猫喵喵的叫
}
}
// 1.定义函数式接口
// 定义: 有且只有一个抽象方法的接口称为函数式接口
// 注解: 约束函数式接口的注解, 不符合要求会报错
@FunctionalInterface
interface Swim {
void swimming();
}
public class LambdaDome {
public staitc void main(String[] args) {
// 2.使用匿名内部类, 创建接口类的子类对象
Swim a = new Swim() {
@Override
public void swimming() {
sout("老师狗爬式游泳")
}
}
// 3. 使用lambda表达式简化匿名内部类写法
Swim a = () -> {
sout("老师狗爬式游泳")
}
a.swimming(); // 老师狗爬式游泳
}
}
- 什么是函数式接口? 有且仅有一个抽象方法的接口。
- 大部分函数式接口,上面都会有 @Functionallnterface 注解, 用于约束当前接口必须是函数式接口。
简化规则
用于进一步简化Lambda表达式的写法。
- 参数类型全部可以省略不写。
-
如果只有一个参数, "()"也可以省略,但多个参数不能省略
-
如果Lambda表达式中只有一行代码,大括号可以不写,同时要省略分号";"
-
如果这行代码是return语句,也必须去掉return。
public class Test {
public static void main(String[] args) {
// 需求: 创建一个登录窗口,窗口上有一个登录按钮
JFrame win = new JFrame("登录窗口");
win.setSize(300, 200);
win.setLocationRelativeTo(null); // 居中展示
win.setDefaultCloseOperaion(JFrame.EXIT_ON_CLOSE);JPanel penel new JPanel(); win.add(penel); JButton btn = new JButton("登录"); // 2.匿名内部类: 快速创建对象, 传给方法 // btn.addActionListener(new ActionListener{ // public void actionPerfirmed(ActionEvent e) { // sout("按钮点击了") // } // }); // 3. 使用lambda表达式的规则简化语法 btn.addActionListener(e -> sout("按钮点击了")); win.setVisible(true); }
}
public class Test {
public static void main(String[] args) {
// 需求: 完成数组排序, 加深匿名内部类的使用
// 1. 准备数组,存放学生对象
student[] students = new student[4];
students[0] = new Student( name:"殷素素",age:35,height:171.5,sex:'女');
students[1] = new Student( name:"杨幂",age:28,height: 168.5,sex:'女');
students[2] = new student( name:"张无忌"age:25,height:181.5,sex:'男');
students[3] = new student( name:"刘亦菲",age: 36,height: 168,sex:'女');// 2.使用数组的API, 对数组按照对象的年龄升序排序 // Arrays.sort(students, new Comparator<Student>() { // @Override // public int compare(Student o1, Student o2) { // // 按对象年龄升序排序 // return o1.getAge() - o2.getAge(); // // 按对象年龄降序排序 // return o2.getAge() - o1.getAge(); // } // }); //3.使用lambda表达式的规则简化语法 Arrays.sort(students, (o1,o2) -> o1.getAge() - o2.getAge()); // 3.遍历数组中的对象并输出 for (int i = 0; i<students.length; i++) { student s = students[i]; sout(s); } }
}
方法引用
进一步简化Lambda表达式, 遇到可以简化场景时使用(IDEA会提示)
静态方法引用
类名::静态方法
如果某个Lambda表达式里只是调用一个静态方法, 并且 "->"前后参数的形式一致, 就可以使用静态方法引用
public class Student {
private String name;
private int age;
private double height;
private String sex;
// 提供静态方法
public static int compareByAge(Student s1, Student s2) {
return s1.getAge() - s2.getAge()
}
}
public class Test {
public static void main(String[] args) {
// 1. 准备数组,存放学生对象
student[] students = new student[4];
students[0] = new Student( name:"殷素素",age:35,height:171.5,sex:'女');
students[1] = new Student( name:"杨幂",age:28,height: 168.5,sex:'女');
students[2] = new student( name:"张无忌"age:25,height:181.5,sex:'男');
students[3] = new student( name:"刘亦菲",age: 36,height: 168,sex:'女');
// 2.使用数组的API, 对数组按照对象的年龄升序排序
// Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());
// 3.使用lambda表达式调用静态方法
// Arrays.sort(students, (o1, o2) -> Student.compareByAge(o1, o2));
// 3.使用静态方法引用进一步简化
Arrays.sort(students, Student::compareByAge);
// 4.遍历数组中的对象并输出
for (int i = 0; i<students.length; i++) {
student s = students[i];
sout(s);
}
}
}
实例方法引用
对象名::实例方法
如果某个Lambda表达式里只是调用一个实例方法, 并且"->"前后参数的形式一致, 就可以使用实例方法引用
public class Student {
private String name;
private int age;
private double height;
private String sex;
// 提供实例方法
public int compareByAge(Student s1, Student s2) {
return s1.getAge() - s2.getAge()
}
}
public class Test {
public static void main(String[] args) {
// 1. 准备数组,存放学生对象
student[] students = new student[4];
students[0] = new Student( name:"殷素素",age:35,height:171.5,sex:'女');
students[1] = new Student( name:"杨幂",age:28,height: 168.5,sex:'女');
students[2] = new student( name:"张无忌"age:25,height:181.5,sex:'男');
students[3] = new student( name:"刘亦菲",age: 36,height: 168,sex:'女');
// 2.使用数组的API, 对数组按照对象的年龄升序排序
// Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());
// 3.使用lambda表达式调用实例方法
Student s1 = new Student();
// Arrays.sort(students, (o1, o2) -> s1.compareByAge(o1,o2));
// 3.使用实例方法引用进一步简化
Arrays.sort(students, s1::compareByAge);
// 4.遍历数组中的对象并输出
for (int i = 0; i<students.length; i++) {
student s = students[i];
sout(s);
}
}
}
特定类型的方法引用
特定类的名称::方法
如果某个Lambda表达式里只是调用一个特定类型的实例方法, 并且前面参数列表中的第一个参数作为方法的主调, 后面的所有参数都是作为该实例方法的入参, 此时可以使用特定类型的方法引用
public class Demo {
public static void main(String[] args) {
// 需求:有一个字符申数组,里面有一些人的名字都是,请按照名字的首字母升序排序
String[] names = {"Tom", "Jerry", "Bobi", "曹操", "Mike", "angela", "Dlei", "Jack", "Rose", "Andy", "caocao"};
// 1.对数组排序: Arrays.sort(数组, 比较器对象)
Arrays.sort(names); // 默认就是按照首字母的编号升序排序
System.out.println(Arrays.toString(names)); // [Andy, Bobi, Jack, Jerry, Mike, Rose, Tom, angela, caocao, 曹操, Dlei]
// 2.需要忽略首字母的大小写进行升序排序(java官网默认是搞不定的,需要自己指定比较规则)
Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2); // java提供了字符串按照首字母忽略大小写比较的方法
}
});
System.out.println(Arrays.toString(names)); // [Andy, angela, Bobi, caocao, Dlei, Jack, Jerry, Mike, Rose, Tom, 曹操]
// 3.使用特定类型的方法引用简化代码
Arrays.sort(names, String::compareToIgnoreCase);
System.out.println(Arrays.toString(names)); // [Andy, angela, Bobi, caocao, Dlei, Jack, Jerry, Mike, Rose, Tom, 曹操]
}
}
构造器引用
类名::new
如果某个Lambda表达式里只是在创建对象, 并且"->"前后参数情况一致, 就可以使用构造器引用
// 汽车类
class Car {
private String name;
}
// 函数式接口
@FunctionalInterface
interface CarFactory {
Car getCar(String name);
}
public class Demo {
public static void main(String[] args) {
// 1.创建接口的匿名内部类对象
CarFactory cf = new CarFactory() {
@Override
public Car getCar(String name) {
return new Car(name);
}
}
// 2.使用lambda表达式简写
CarFactory cf = name -> new Car(name);
// 3.构造器引用进一步简化
CarFactory cf = Car::new;
Car c1 = cf.getCar("奔驰");
System.out.println(c1); // Car(name=奔驰)
}
}
Stream流
认识流
Jdk8开始新增的一套API(iava.util.stream.*),可以用于操作集合或者数组的数据
- 先得到集合或者数组的Stream流。
- 然后调用Stream流的方法对数据进行处理。
- 获取处理的结果。
示例代码
public class Dome {
public static void main(String[] args) {
// 1.认识stream流的使用
// 需求: 把集合中所有以"张"开头,且是3个字的元素存储到一个新的集合
ArrayList<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
// 2.使用传统方式完成需求
List<String> newList = new ArrayList<>();
for (String l : list) {
if (l.startsWith("张") && l.length() == 3) {
newList.add(l);
}
}
// [张无忌, 张三丰]
System.out.println(newList);
// 3.使用stream流完成需求
List<String> newList2 = list.stream()
.filter(l -> l.startsWith("张"))
.filter(l -> l.length() == 3)
.collect(Collectors.toList());
// [张无忌, 张三丰]
System.out.println(newList2);
}
}
- Stream流大量的结合了Lambda的语法风格来编程,功能强大,性能高效,代码简洁,支持链式编程
获取流
1.获取集合的Stream流
public class Dome2 {
public static void main(String[] args) {
// 获取Stream流
// 1.获取Collection集合流
// 使用Collection接口提供的stream()方法获取流
Collection<String> list = new ArrayList<>();
Stream<String> s1 = list.stream();
// 2.获取Map集合流
Map<String, Integer> map = new HashMap<>();
// 获取键流
Stream<String> s2 = map.keySet().stream();
// 获取值流
Stream<Integer> s3 = map.values().stream();
// 获取键值对流
Stream<Map.Entry<String, Integer>> s4 = map.entrySet().stream();
}
}
2.获取数组的Stream流
public class Dome2 {
public static void main(String[] args) {
// 获取Stream流
String[] arr = {"a", "b", "c", "d"};
// 3.获取数组流
// 使用Stream类中的静态方法of()
Stream<String> s5 = Stream.of(arr);
// 使用Arrays类中的静态方法stream()
Stream<String> s6 = Arrays.stream(arr);
}
}
处理流
中间方法指的是调用完成后会返回新的Stream流,可以继续使用(支持链式编程),
public class Dome3 {
public static void main(String[] args) {
// 使用stream流的中间方法处理数据
ArrayList<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
// 1.过滤方法
list.stream()
.filter(s -> s.startsWith("张") && s.length() == 3)
.forEach(System.out::println); // 张无忌, 张三丰
// 2.排序方法
List<Double> scores = new ArrayList<>();
scores.add(99.9);
scores.add(66.1);
scores.add(88.7);
scores.add(66.1);
scores.add(72.3);
scores.add(88.7);
// 默认是升序排序 66.1 72.3 88.7 99.9
scores.stream()
.sorted()
.forEach(System.out::println);
// 指定排序规则(降序) 99.9 88.7 72.3 66.1
scores.stream()
.sorted((o1, o2) -> Double.compare(o2, o1))
.forEach(System.out::println);
// 3.截取方法,
// 只要前3名 99.9 88.7 72.3
scores.stream()
.sorted((o1, o2) -> Double.compare(o2, o1))
.limit(3)
.forEach(System.out::println);
// 跳过前2名 72.3 66.1
scores.stream()
.sorted((o1, o2) -> Double.compare(o2, o1))
.skip(2)
.forEach(System.out::println);
// 4.去重方法 99.9 66.1 88.7 72.3
// 如果自定义对象需要去重, 该对象必须重写hashCode和equals方法
scores.stream()
.distinct()
.forEach(System.out::println);
// 5.映射/加工方法: 把流里面的元素进行加工, 得到新的集合
// 成绩是:99.9分 成绩是:66.1分 成绩是:88.7分 成绩是:66.1分 成绩是:72.3分 成绩是:88.7分
scores.stream()
.map(s -> "成绩是:" + s + "分")
.forEach(System.out::println);
// 6.合并流
Stream<String> s1 = Stream.of("张无忌", "赵敏", "张三丰");
Stream<Integer> s2 = Stream.of(11, 22, 31);
Stream<Object> s3 = Stream.concat(s1, s2);
System.out.println(s3.count()); // 6
}
}
终结流
使用Stream是为了方便的操作集合和数组, 操作完成后把结果收集到数组或集合中, 才是最终的目的
- 终结流
- 补充: Optional容器中的元素需要通过get()方法获取出来
- 收集流
代码示例
public class Teacher {
private String name;
private int age;
private double salary;
//... 省略构造器和get/set方法 ...
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}' + '\n';
}
}
public class Dome4 {
public static void main(String[] args) {
// 掌握终结Stream流的方法
List<Teacher> list = List.of(
new Teacher("张三", 18, 5000),
new Teacher("李四", 28, 6000),
new Teacher("王五", 38, 7000),
new Teacher("赵六", 48, 8000)
);
// 1.遍历终结
// Teacher{name='王五', age=38, salary=7000.0} Teacher{name='赵六', age=48, salary=8000.0}
list.stream().filter(t -> t.getSalary() > 6000).forEach(System.out::println);
// 2.获取结果数量
long count = list.stream().filter(t -> t.getSalary() > 6000).count();
System.out.println(count); // 2
// 3.获取最大值 (根据工资)
Teacher max = list.stream().max(Comparator.comparing(Teacher::getSalary)).get();
System.out.println(max); // Teacher{name='赵六', age=48, salary=8000.0}
// 4.获取最小值 (根据年龄)
Teacher min = list.stream().min(Comparator.comparing(Teacher::getAge)).get();
System.out.println(min); // Teacher{name='张三', age=18, salary=5000.0}
}
}
public class Dome5 {
public static void main(String[] args) {
// 掌握收集Stream流的方法
List<Teacher> list = List.of(
new Teacher("张三", 18, 5000),
new Teacher("李四", 28, 6000),
new Teacher("王五", 38, 7000),
new Teacher("赵六", 48, 8000)
);
// 1. 把流收集到list集合中
List<Teacher> list1 = list.stream().
filter(s -> s.getName().startsWith("张")).
collect(Collectors.toList());
System.out.println(list1); // [Teacher{name='张三', age=18, salary=5000.0}]
// 2. 把流收集到set集合中
Set<Teacher> list2 = list.stream().
filter(s -> s.getName().startsWith("张"))
.collect(Collectors.toSet());
System.out.println(list2);// [Teacher{name='张三', age=18, salary=5000.0}]
// 3. 把流收集到数组中
Object[] list3 = list.stream()
.filter(s -> s.getName().startsWith("张"))
.toArray();
System.out.println(Arrays.toString(list3)); // [Teacher{name='张三', age=18, salary=5000.0}]
// 4. 把流收集到Map集合中: 键是老师的名字, 值是老师的薪水
// 4.1完整写法
// Map<String, Double> map = list.stream().collect(Collectors.toMap(new Function<Teacher, String>() {
// @Override
// public String apply(Teacher teacher) {
// return teacher.getName();
// }
// }, new Function<Teacher, Double>() {
// @Override
// public Double apply(Teacher teacher) {
// return teacher.getSalary();
// }
// }));
// 4.2lambda简写
// Map<String, Double> map = list.stream().
// collect(Collectors.toMap(teacher -> teacher.getName(), teacher -> teacher.getSalary()));
// 4.3方法引用简写
Map<String, Double> map = list.stream().
collect(Collectors.toMap(Teacher::getName, Teacher::getSalary));
System.out.println(map); // {李四=6000.0, 张三=5000.0, 王五=7000.0, 赵六=8000.0}
}
}