Lambda表達式
為什么使用Lambda表達式?
Lambda 是一個匿名函數,我們可以把Lambda表達式理解為是一段可以傳遞的代碼。可以寫出更簡潔、高效的代碼。
Lambda初體驗
我們先來看一段匿名內部類的代碼 使用Lambda表達式后的樣子
//匿名內部類
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
TreeSet<Integer> treeSet = new TreeSet<>(com);
//使用了Lambda式后的匿名內部類
Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
TreeSet<Integer> treeSet = new TreeSet<>(com);
准備工作: 創建一個Employee 實體類
public class Employee {
private String name;
private Integer age;
private Double salary;
private Status status;
//get\set\equals\hashcode方法
`````````````
`````````````
public enum Status{
FREE,BUSY,VACATION;
}
}
接下來假設我們有一個需求: 獲取公司員工年齡大於等於35的員工信息
那么我們可能需要這么干
public class TestLambda {
List<Employee> list = Arrays.asList(
new Employee("張三", 18, 9999.99),
new Employee("李四", 38, 5555.99),
new Employee("王五", 50, 6666.99),
new Employee("趙六", 16, 3333.33),
new Employee("田七", 8, 7777.77)
);
//傳統方式
@Test
public void test1(){
List<Employee> empList = filterEmployeeByAge(list);
for (Employee e:empList) {
System.out.println(e);
}
}
public List<Employee> filterEmployeeByAge(List<Employee> list){
List<Employee> emps = new ArrayList<>();
for(Employee e:list){
if(e != null && e.getAge() >= 35)
emps.add(e);
}
return emps;
}
}
然后來了第二個需求:獲取工資大於的5000的員工,而此時原來的方法可能被別的方法引用着
所以我們又這么干,新加了一個方法滿足第二個需求
public List<Employee> filterEmployeeBySalary(List<Employee> list){
List<Employee> emps = new ArrayList<>();
for(Employee e:list){
if(e != null && e.getSalary() >= 5000)
emps.add(e);
}
return emps;
}
那么現在有一個問題,如果接下來有第三、第四或第五個需求,我們需要創建第三、第四、第五個方法么
這好像是一件十分愚蠢的事
所以接下來我們使用策略模式來做一次優化 , 那這跟Lambda表達式有半毛錢關系么,別急,耐心看下去
public interface MyPredicate<T> {
public boolean test(T t);
}
public class FilterEmployeeByAge implements MyPredicate<Employee>{
@Override
public boolean test(Employee t) {
return t == null ? false:t.getAge() >= 35;
}
}
public class TestLambda {
List<Employee> list = Arrays.asList(
new Employee("張三", 18, 9999.99),
new Employee("李四", 38, 5555.99),
new Employee("王五", 50, 6666.99),
new Employee("趙六", 16, 3333.33),
new Employee("田七", 8, 7777.77)
);
@Test
public void test2(){
List<Employee> empsList = filterEmployee(list,new FilterEmployeeByAge());
for(Employee e:empsList){
System.out.println(e);
}
}
//優化方式一:策略設計模式
public List<Employee> filterEmployee(List<Employee> list,MyPredicate<Employee> mp){
List<Employee> emps = new ArrayList<>();
for (Employee e:list) {
if(mp.test(e)){
emps.add(e);
}
}
return emps;
}
}
不知道大家看明白了沒有,如果我有新的需求,我只需要去改變,我的實現接口就可以了,無需創建新的方法
但是有人可能會說,好麻煩呀,我每次想用一次filterEmployee方法去完成不同的需求,我就務必要創建一個新的接口實現類,而且實現類中,還就一個方法,一點也不可愛
好的,接下來我們這么干,使用匿名內部類
@Test
public void test3(){
//匿名內部類
List<Employee> empsList = filterEmployee(list,new MyPredicate<Employee>() {
@Override
public boolean test(Employee t) {
return t == null ? false:t.getSalary() < 5000 ;
}
});
for(Employee em:empsList){
System.out.println(em);
}
}
//優化方式二:策略設計模式
public List<Employee> filterEmployee(List<Employee> list,MyPredicate<Employee> mp){
List<Employee> emps = new ArrayList<>();
for (Employee e:list) {
if(mp.test(e)){
emps.add(e);
}
}
return emps;
}
接下來大家應該想到了點什么,匿名內部類 到 Lambda
@Test
public void test4(){
List<Employee> empsList = filterEmployee(list,(e) -> e == null ? false:e.getSalary() < 5000 );
empsList.forEach(System.out::println);
}
Lambda表達式的基礎語法
Lambda表達式的基礎語法: Java8中引入了一個新的操作符 "—>" 該操作符稱為箭頭操作符 或 Lambda操作符,箭頭操作符將Lambda表達式 拆分成兩個部分
左側:Lambda表達式的參數列表
右側:Lambda表達式中所需執行的功能 即Lambda體
語法格式一:無參數,無返回值
Runnable ru = () -> System.out.println("實現Runnable的接口的Lambda");
ru.run();
語法格式二:有一個參數,且無返回值 Consumer
co = (x) -> System.out.println(x); co.accept("呵呵");
語法格式三:若只有一個參數,小括號可以使用不寫 x -> System.out.println(x);
語法格式四:有兩個以上的參數,有返回值,並且Lambda體中有多條語句 Comparator
com = (x,y) -> {
System.out.println("函數式接口");
return Integer.compare(x,y);
};
語法格式五:Lambda體中只有一條語句,return和大括號可以省略不寫 Comparator
com = (x,y) -> Integer.compare(x,y);
語法格式六:Lambda 表達式的參數列表的數據類型可以省略不寫,因為JVM可以推斷出,數據類型,即類型推斷 Comparator
com = (Integer x,Integer y) -> Integer.compare(x,y); 注:數據類型要么全寫 要么全不寫
Lambda 表達式需要"函數式的接口"的支持
函數式接口:接口中只有一個抽象方法的接口,成為函數式接口,可以使用注解 @FunctionalInterface修飾
可以檢查是否是函數式接口
Java8內置的四大函數式接口
Consumer<T> : 消費型接口 void accept(T t);
Supplier<T> : 供給型接口 T get();
Function<T, R> :函數式接口 R apply(T t);
Predicate<T> : 斷言型接口 boolean test(T t);
public class TestLambda3 {
// Consumer<T> 消費型接口
@Test
public void test1() {
happy(10000, x -> System.out.println("我今天花了" + x + "元"));
}
public void happy(double money, Consumer<Double> con) {
con.accept(money);
}
// Supplier<T> 供給型接口
@Test
public void test2() {
List<Integer> numList = getNumList(10,() -> (int)(100*Math.random()));
for(Integer i:numList){
System.out.println(i);
}
}
// 需求:產生指定個數的整數 放到集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer e = sup.get();
list.add(e);
}
return list;
}
//Functional<T> 函數式接口
@Test
public void test3(){
String string = strHandler(" 我前面后面都有空格額 ",(str) -> str.trim());
System.out.println(string);
}
//需求:用於處理字符串
public String strHandler(String str,Function<String, String> fuc){
return fuc.apply(str);
}
//Predicate<T> 斷言型接口
@Test
public void test4(){
List<String> list = Arrays.asList("Hello","hi","Apple","zoo","beautiful");
List<String> result = filterStr(list, (str) -> str.length() > 3);
for(String s:result){
System.out.println(s);
}
}
//需求:將滿足條件的字符串放入集合
public List<String> filterStr(List<String> list,Predicate<String> pre){
List<String> strList = new ArrayList<>();
for(String str:list){
if(pre.test(str)){
strList.add(str);
}
}
return strList;
}
}
當然還有其他函數式接口
Lambda體中的方法引用與構造器引用
(一)、方法引用
若Lambda 體中的內容有方法已經實現了,我們可以使用 "方法引用",(可以理解方法引用為Lambda的另一種表現形式)
主要有三種語法格式:對象 : : 實例方法名
類 : : 靜態方法名
類 : : 實例方法名
注意:
①、Lambda 體中調用方法的參數列表與返回值類型,要與函數式接口中抽象方法的函數列表和返回值類型保持一致
②、若 Lambda 參數列表中的第一個參數是 實例方法的調用者,而第二個參數是實例方法的參數時,可以使用ClassName : : methodName
(二)、構造器引用格式:ClassName : : New
注意:需要 調用的構造器的參數列表要與函數式接口中抽象方法的參數列表保持一致!
(三)、數組引用格式:Type[ ]::new
public class TestMethodRef {
//對象::實例方法名
@Test
public void test1(){
Consumer<String> con = (x) -> System.out.println(x);
PrintStream ps = System.out;
Consumer<String> con1 = ps::println;
Consumer<String> con2 = System.out::println;
con2.accept("abcd");
}
//對象::實例方法名
@Test
public void test2(){
Employee emp = new Employee();
Supplier<String> sup = () -> emp.getName();
Supplier<String> sup1 = emp::getName;
}
//類::靜態方法名
@Test
public void test3(){
Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
Comparator<Integer> com1 = Integer::compare;
}
//類::實例方法名
@Test
public void test4(){
BiPredicate<String, String> bp = (x,y) -> x.equals(y);
BiPredicate<String, String> bp1 = String::equals;
}
//構造器引用
@Test
public void test5(){
Supplier<Employee> sup = () -> new Employee();
//構造器引用方式 調用哪個構造器取決於 接口的參數類型
Supplier<Employee> sup2 = Employee::new;
Employee employee = sup2.get();
System.out.println(employee);
}
//數組引用
@Test
public void test6(){
Function<Integer, String[]> fun = (x) -> new String[x];
String[] strs = fun.apply(10);
System.out.println(strs.length);
Function<Integer, String[]> fun2 = String[]::new;
String[] apply = fun2.apply(20);
System.out.println(apply.length);
}
}
Stream API
簡介:
Java8中有兩大最為重要的改變。第一個是 Lambda 表達式;另外一個則是 Stream API(java.util.stream.*)。
Stream 是 Java8 中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常復雜的查找、過濾和映射數據等操作。
使用Stream API 對集合數據進行操作,就類似於使用 SQL 執行的數據庫查詢。也可以使用 Stream API 來並行執行操作。簡而言之,Stream API 提供了一種高效且易於使用的處理數據的方式
流(Stream) 到底是什么?
是數據渠道,用於操作數據源(集合、數組等)所生成的元素序列.
“集合講的是數據,流講的是計算!”
注意:
①、Stream 自己不會存儲元素
②、Stream 不會改變源對象。相反它們會返回一個持有對象的結果
③、Stream 操作是延遲執行的,這意味着它們會等到需要結果的時候才會執行
Stream的三個操作步驟
1. 創建 Stream
2. 中間操作
3. 終止操作
創建Stream
public class TestStreamAPI {
//創建Stream
@Test
public void test1(){
//1.可以通過 Collection 系列集合提供的stream() 或 parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//2.通過 Arrays 中的靜態方法 stream() 獲取數組流
Employee[] emps = new Employee[10];
Stream<Employee> stream2 = Arrays.stream(emps);
//3.通過 Stream 類中的靜態方法 of()
Stream<String> stream3 = Stream.of("aa","bb","cc");
//4.創建無限流
//迭代
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x+2);
//stream4.forEach(System.out::println);
//生成
Stream.generate(() -> Math.random()).limit(5).forEach(System.out::println);
}
}
中間操作
public class TestStreamAPI {
List<Employee> employees = Arrays.asList(
new Employee("張三", 18, 9999.99),
new Employee("李四", 38, 5555.99),
new Employee("王五", 50, 6666.99),
new Employee("王五", 50, 6666.99),
new Employee("王五", 50, 6666.99),
new Employee("趙六", 16, 3333.33),
new Employee("田七", 8, 7777.77)
);
/**
* 篩選與切片
* filter-接收 Lambda, 從流中排除某些元素。
* limit-截斷流,使其元素不超過給定數量
* skip(n) - 跳過元素,返回了一個扔掉了前 n 個元素的流.若流中元素不足n個,則返回一個空流.與limit互補
* distinct-篩選,跳過流所生成元素的 hashCode() 和 equals() 去除重復元素
* -此方法務必需要重寫equals 和 hashcode 方法
*/
@Test
public void test2(){
//中間操作:不會執行任何操作
Stream<Employee> stream = employees.stream().filter((e) -> {
System.out.println("Stream API的中間操作");
return e.getAge() > 20;
});
//終止操作:一次執行全部內容,即"惰性求值"
stream.forEach(System.out::println);
}
@Test
public void test3(){
employees.stream().filter((e) -> {
System.out.println("短路");
return e.getSalary() > 3000;
}).limit(2).forEach(System.out::println);
}
@Test
public void test4(){
employees.stream().filter(e -> e.getSalary() >3000).skip(2).forEach(System.out::print);
}
@Test
public void test5(){
employees.stream().filter((e) -> e.getSalary() > 5000).skip(2).distinct().forEach(System.out::println);
}
}
public class TestStreamAPI2 {
List<Employee> employees = Arrays.asList(
new Employee("張三", 18, 9999.99),
new Employee("李四", 38, 5555.99),
new Employee("王五", 50, 6666.99),
new Employee("王五", 50, 6666.99),
new Employee("王五", 50, 6666.99),
new Employee("趙六", 16, 3333.33),
new Employee("田七", 8, 7777.77)
);
/**
* 映射:
* map - 接收Lambda,將元素轉換成 其他形式或提取信息.接收一個函數作為參數,該函數會被應用到每個元素上,並將其映射成一個新的元素
* flatMap - 接收一個函數作為參數,將流中的每個值都換成另一個流,然后把所有流,連接成一個新流
*/
@Test
public void test2(){
List<String> list = Arrays.asList("aa","bb","cc","dd","ee");
// list.stream().map((str) -> str.toUpperCase()).forEach(System.out::println);
employees.stream().map(e -> e.getName()).forEach(System.out::println);
System.out.println("--------------------------------------");
Stream<Stream<Character>> stream = list.stream().map(TestStreamAPI2::filterCharacter);
stream.forEach(sm -> sm.forEach(System.out::print));
System.out.println("\n--------------------------------------");
Stream<Character> stream2 = list.stream().flatMap(TestStreamAPI2::filterCharacter);
stream2.forEach(System.out::print);
}
public static Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for(Character ch:str.toCharArray()){
list.add(ch);
}
return list.stream();
}
/**
* 排序
* sorted-自然排序(Comparable)
* sorted(Comparable com)-定制排序(Comparator)
*/
@Test
public void test3(){
List<String> list = Arrays.asList("ccc","aaa","bbb","ddd");
list.stream().sorted().forEach(System.out::println);
System.out.println("----------------------------");
employees.stream().sorted((e1,e2) -> {
if(e1.getAge().equals(e2.getAge())){
return e1.getSalary().compareTo(e2.getSalary());
}else{
return e1.getAge().compareTo(e2.getAge());
}
}).forEach(System.out::println);;
}
}
終止操作
/**
*
* 終止操作
*
*/
public class TestStreamAPI3 {
List<Employee> employees = Arrays.asList(
new Employee("張三", 18, 9999.99,Status.BUSY),
new Employee("李四", 38, 5555.99,Status.FREE),
new Employee("王五", 50, 6666.99,Status.VACATION),
new Employee("王五", 50, 6666.99,Status.BUSY),
new Employee("王五", 50, 6666.99,Status.BUSY),
new Employee("趙六", 16, 3333.33,Status.FREE),
new Employee("田七", 8, 7777.77,Status.VACATION)
);
/**
* 查找與匹配
* allMatch-檢查是否匹配所有元素
* anyMatch-檢查是否至少匹配一個元素
* noneMatch-檢查是否沒有匹配元素
* findFirst-返回第一個元素
* findAny-返回當前流中的任意元素
* count-返回流中元素的總個數
* max-返回流中最大值
* min-返回流中最小值
*/
@Test
public void test1(){
//檢查所有員工是否 都為忙碌狀態
boolean allMatch = employees.stream().allMatch(e -> e.getStatus().equals(Status.BUSY));
System.out.println(allMatch);
//檢查是否有一個員工為忙碌狀態
boolean anyMatch = employees.stream().anyMatch(e -> e.getStatus().equals(Status.BUSY));
System.out.println(anyMatch);
//檢查是否 沒用員工處於忙碌狀態
boolean noneMatch = employees.stream().noneMatch(e -> e.getStatus().equals(Status.BUSY));
System.out.println(noneMatch);
//查詢 工資排在首位的 員工
Optional<Employee> findFirst = employees.stream().sorted((e1,e2) -> -Double.compare(e1.getSalary(), e2.getSalary())).findFirst();
System.out.println(findFirst.get());
//查找任意 一個處於空閑狀態的員工
Optional<Employee> findAny = employees.parallelStream().filter(e -> e.getStatus().equals(Status.FREE)).findAny();
System.out.println(findAny.get());
//查詢員工的個數
long count = employees.stream().count();
System.out.println(count);
//查詢 拿最大工資的員工
Optional<Employee> max = employees.stream().max((e1,e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(max.get());
//查詢最小的工資數
Optional<Double> minSalary = employees.stream().map(Employee::getSalary).min(Double::compare);
System.out.println(minSalary);
}
/**
* 歸約: reduce(T identity, BinaryOperator)/reduce(BinaryOperator)
* -可以將流中元素反復結合起來得到一個值
*/
@Test
public void test2() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
// 有起始值 所有不可能為空 故返回值為Integer
Integer sum = list.stream().reduce(0, (x, y) -> (x + y));
System.out.println(sum);
System.out.println("------------------------");
Optional<Double> salarySum = employees.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(salarySum.get());
}
/**
* 收集 collect-將流轉換成其他形式,接收一個Collector接口的實現,用於給Stream中元素作匯總的方法
*/
@Test
public void test3() {
List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("------------------------");
Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());
System.out.println("------------------------");
HashSet<String> hashSet = employees.stream().map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hashSet.forEach(System.out::print);
}
@Test
public void test4(){
//總數
Long count = employees.stream().collect(Collectors.counting());
System.out.println(count);
System.out.println("-------------------------------");
//平均值
Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
//總和
Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
//最大值
Optional<Employee> max = employees.stream().collect(Collectors.maxBy((e1,e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(max.get());
//最小值
Optional<Double> min = employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));
System.out.println(min.get());
}
//分組
@Test
public void test5(){
//單級分組
Map<Status, List<Employee>> map = employees.stream().collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
//多級分組
Map<Status, Map<String, List<Employee>>> map2 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus,Collectors.groupingBy(e -> {
if(((Employee)e).getAge() < 18){
return "未成年";
}else{
return "成年";
}
})));
System.out.println(map2);
}
//分區
@Test
public void test6(){
Map<Boolean, List<Employee>> part = employees.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 8000));
System.out.println(part);
}
@Test
public void test7(){
DoubleSummaryStatistics doubleSummaryStatistics = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(doubleSummaryStatistics.getSum());
System.out.println(doubleSummaryStatistics.getAverage());
System.out.println(doubleSummaryStatistics.getMax());
}
}