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() {@Overridepublic 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() {@Overridepublic 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>() {@Overridepublic 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() {@Overridepublic 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.9scores.stream().sorted().forEach(System.out::println);// 指定排序規則(降序) 99.9 88.7 72.3 66.1scores.stream().sorted((o1, o2) -> Double.compare(o2, o1)).forEach(System.out::println);// 3.截取方法,// 只要前3名 99.9 88.7 72.3scores.stream().sorted((o1, o2) -> Double.compare(o2, o1)).limit(3).forEach(System.out::println);// 跳過前2名 72.3 66.1scores.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方法 ...@Overridepublic 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}}
}