Lambda表示式總結
Lambda表示式總結
- 使用範例以及例子
Lambda
表示式基礎語法- 函式式介面
Lambda
練習Java8
四大內建函式式介面- 方法引用和構造器引用
使用範例以及例子
使用匿名內部類:
Comparator<Integer>com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) { //降序排列
return Integer.compare(o2,o1);
}
};
使用Lambda
表示式:
Comparator<Integer> com = (x, y) -> Integer.compare(y, x);
下面給出一個例子來引入Lambda
表示式。
給出一個Employee
類,有name、age、salary
三個屬性:
public class Employee {
private String name;
private int age;
private double salary;
public Employee() {
}
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public double getSalary() {
return salary;
}
@Override
public String toString() {
return "name='" + name + '\'' +
", age=" + age +
", salary=" + salary;
}
}
然後我們需要通過限制查詢資料:
- 比如查詢年齡
>25
歲的所有員工的資訊; - 再如查詢工資
>4000
的員工資訊;
首先給出一個List
集合類模擬資料庫表:
//將陣列轉換成集合的
List<Employee> employees = Arrays.asList(
new Employee("張三",23,3333.33),
new Employee("李四",24,4444.44),
new Employee("王五",25,5555.55),
new Employee("趙六",26,6666.66),
new Employee("田七",27,7777.77)
);
原始方法
然後我們寫分別查詢出年齡大於25
歲的員工資訊和工資大於4000
的員工資訊,發現findEmployeesByAge
和findEmployeesBySalary
兩個方法程式碼非常的相似,只有查詢條件不同,所以這個方法是不太可取的。
public void test3(){
//年齡
List<Employee> list = findEmployeesByAge(employees);
for(Employee emp : list){
System.out.println(emp);
}
//工資
System.out.println("---------------------");
List<Employee> list2 = findEmployeesBySalary(employees);
for(Employee emp : list2){
System.out.println(emp);
}
}
//原始方法 : 查詢出年齡大於25歲的(這個是最原始的方法)
public List<Employee> findEmployeesByAge(List<Employee>list){
List<Employee>emps = new ArrayList<>();
for(Employee emp : list){
if(emp.getAge() > 25){
emps.add(emp);
}
}
return emps;
}
//原始方法 : 查詢出工資大於4000的(這個是最原始的方法)
//和上面的方法唯一的差別只有年齡和工資的改動,程式碼冗餘
public List<Employee> findEmployeesBySalary(List<Employee>list){
List<Employee>emps = new ArrayList<>();
for(Employee emp : list){
if(emp.getSalary() > 4000){
emps.add(emp);
}
}
return emps;
}
優化方式一-使用策略模式來優化
策略模式需要行為演算法族,於是我們建立查詢行為的介面MyPredicate<T>
:
public interface MyPredicate <T>{
public boolean test(T t);
}
並建立相關的實現類代表不同的演算法行為: (分別是年齡> 25
和工資> 4000
的 ):
public class FilterEmployeeByAge implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getAge() > 25;
}
}
public class FilterEmployeeBySalary implements MyPredicate<Employee>{
@Override
public boolean test(Employee employee) {
return employee.getSalary() >= 4000;
}
}
這時我們可以只需要建立通用的方法: 具體的呼叫只需要傳入具體的實現類(介面作為引數)
public List<Employee> filterEmployees(List<Employee>list,MyPredicate<Employee>mp){
List<Employee>emps = new ArrayList<>();
for(Employee emp : list){
if(mp.test(emp)){ //呼叫相應的過濾器
emps.add(emp);
}
}
return emps;
}
測試的時候就傳入兩個不同的類,來指定查詢的行為
//優化方式一 : 使用策略設計模式進行優化 下面的方法只要寫一個
public void test4(){
List<Employee> list = filterEmployees(this.employees, new FilterEmployeeByAge());
for(Employee emp : list){
System.out.println(emp);
}
System.out.println("------------------------");
List<Employee> list2 = filterEmployees(this.employees, new FilterEmployeeBySalary());
for(Employee emp : list2){
System.out.println(emp);
}
}
優化方式二-使用匿名內部類優化
這樣的好處在於不需要建立介面的具體的實現類,(但是還是需要MyPredicate
介面和filterEmployees()
方法):
//優化方式二 : 使用匿名內部類 這樣的好處是不要建立一個額外的 策略類
public void test5(){
List<Employee> list = filterEmployees(this.employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getSalary() > 4000;
}
});
for (Employee emp:list) {
System.out.println(emp);
}
}
優化方式三-使用Lambda表示式
省去匿名內部類的沒用的程式碼,增強可讀性:(注意還是需要那個filterEmployees方法)
public void test6(){
List<Employee> list = filterEmployees(this.employees, (e) -> e.getSalary() > 4000);
list.forEach(System.out::println);
}
優化方式四-使用Stream-API
使用StreamAPI
完全不需要其他的程式碼,包括不需要filterEmployees()
方法,程式碼很簡潔:
public void test7(){
employees.stream().filter( (e) -> e.getSalary() < 4000 ).limit(2).forEach(System.out::println);
System.out.println("------------------");
employees.stream().map(Employee::getName).forEach(System.out::println); //列印所有的名字
}
Lambda表示式基礎語法
關於箭頭操作符:
Java8
中引入了一個新的操作符,"->"
,該操作符稱為箭頭操作符或者Lambda
操作符,箭頭操作符將Lambda
表示式拆分成兩部分;- 左側:
Lambda
表示式的引數列表,對應的是介面中抽象方法的引數列表; - 右側:
Lambda
表示式中所需要執行的功能(Lambda
體),對應的是對抽象方法的實現;(函式式介面(只能有一個抽象方法)) Lambda
表示式的實質是 對介面的實現;
語法格式:
- (一) 介面中的抽象方法 : 無引數,無返回值;
例如: Runnable
介面中的run
方法:
public void test1(){
/*final */int num = 2; //jdk1.7之前必須定義為final的下面的匿名內部類中才能訪問
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello world!" + num); //本質還是不能對num操作(只是jdk自己為我們設定成了final的)
}
};
r.run();
System.out.println("----------使用Lambda輸出-----------");
Runnable r1 = () -> System.out.println("Hello world!" + num);
r1.run();
}
- (二) 介面中的抽象方法 : 一個引數且無返回值; (若只有一個引數,那麼小括號可以省略不寫)
public void test2(){
// Consumer<String>con = (x) -> System.out.println(x);
Consumer<String>con = x -> System.out.println(x);
con.accept("Lambda牛逼!");
}
- (三) 兩個引數,有返回值,並且有多條語句 : 要用大括號括起來,而且要寫上
return
public void test3(){
Comparator<Integer>com = (x,y) -> {
System.out.println("函式式介面");
return Integer.compare(y,x); //降序
};
Integer[] nums = {4,2,8,1,5};
Arrays.sort(nums,com);
System.out.println(Arrays.toString(nums));
}
輸出:
函式式介面
函式式介面
函式式介面
函式式介面
函式式介面
函式式介面
函式式介面
函式式介面
函式式介面
[8, 5, 4, 2, 1]
- (四) 兩個引數,有返回值,但是隻有一條語句: 大括號省略,
return
省略
public void test4(){
Comparator<Integer>com = (x,y) -> Integer.compare(x,y);//升序
Integer[] nums = {4,2,8,1,5};
Arrays.sort(nums,com);
System.out.println(Arrays.toString(nums));
}
輸出:
[1, 2, 4, 5, 8]
- (五)
Lambda
表示式的引數列表的資料型別 可以省略不寫,因為JVM編譯器通過上下文推斷出資料型別,即"型別推斷",(Integer x,Integer y ) -> Integer.compare(x,y)
可以簡寫成(x,y) -> Integer.compare(x,y)
;
上聯: 左右遇一括號省
下聯: 左側推斷型別省
橫批: 能省則省
函式式介面
- 若介面中只有一個抽象方法的介面稱為函式式介面;
- 可以使用註解
@FunctionlInterface
來標識,可以檢查是否是函式式介面;
例子: 對一個進行+-*/
的運算:
函式式介面:
@FunctionalInterface //函式式介面
public interface MyFunction {
public Integer getValue(Integer num);
}
通用函式:
public Integer operation(Integer num,MyFunction mf){
return mf.getValue(num);
}
測試:
public void test5(){
Integer res = operation(200, (x) -> x * x);
System.out.println(res);
}
Lambda練習
練習一-Employee
類中先按年齡比,年齡相同按照姓名比-都是升序
先給出集合,模擬資料庫表:
List<Employee> employees = Arrays.asList(
new Employee("田七",27,7777.77),
new Employee("王五",24,5555.55),
new Employee("張三",23,3333.33),
new Employee("李四",24,4444.44),
new Employee("趙六",26,6666.66)
);
public void test1(){
Collections.sort(employees,(x,y) ->{
if(x.getAge() == y.getAge()){
return x.getName().compareTo(y.getName());
}else{
return Integer.compare(x.getAge(),y.getAge());
}
});
for (Employee emp: employees) {
System.out.println(emp);
}
}
輸出:
name='張三', age=23, salary=3333.33
name='李四', age=24, salary=4444.44
name='王五', age=24, salary=5555.55
name='趙六', age=26, salary=6666.66
name='田七', age=27, salary=7777.77
練習二-宣告一個帶兩個泛型的介面,並且對兩個Long
型數值計算
@FunctionalInterface
public interface MyCalFunction<T,R> {
public R getValue(T t1,T t2);
}
對應函式和測試:
public void test3(){
op(200L,200L,(x,y) -> x + y);
op(200L,200L,(x,y) -> x * y);
}
public void op(Long l1,Long l2,MyCalFunction<Long,Long>mc){//需求: 對於兩個long型運算進行處理
System.out.println(mc.getValue(l1, l2));
}
更多的例子: (取自<<
Java8實戰>>
)
(注意型別可以省略(型別推導))。
上圖的Apple
類:
public class Apple {
public String color;
public int weight;
public Apple() {
}
public Apple(String color, int weight) {
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
Java8四大內建函式式介面
我們發現,如果使用Lambda
還要自己寫一個介面的話太麻煩,所以Java
自己提供了一些介面:
Consumer< T >con
消費性 介面:void accept(T t)
;Supplier< T >sup
供給型介面 :T get()
;Function< T , R >fun
函式式介面 :R apply (T t)
;Predicate< T >
: 斷言形介面 :boolean test(T t)
;
Consumer< T >con
消費性介面-void accept(T t)
@Test
public void test1(){
apply(1000,(num) -> System.out.println("消費了" + num + "元!"));
}
public void apply(double num,Consumer<Double>con){
con.accept(num);
}
Supplier< T >sup
供給型介面-T get()
例子: 產生指定個數的整數,並放入集合中;
public void test2(){
ArrayList<Integer> res = getNumList(10, () -> (int) (Math.random() * 100));
System.out.println(res);
}
//需求,產生指定個數的整數,並放入集合中
public ArrayList<Integer> getNumList(int num, Supplier<Integer>sup){
ArrayList<Integer>list = new ArrayList<>();
for(int i = 0; i < num; i++){
Integer e = sup.get();
list.add(e);
}
return list;
}
Function< T, R >fun
函式式介面-R apply (T t)
public void test3(){
String newStr = strHandler("abc", (str) -> str.toUpperCase());
System.out.println(newStr);
newStr = strHandler(" abc ", (str) -> str.trim());
System.out.println(newStr);
}
public String strHandler(String str, Function<String,String>fun){
return fun.apply(str);
}
Predicate< T >
斷言形介面-boolean test(T t)
判斷一些字串陣列判斷長度>2
的字串:
public void test4(){
List<String> list = Arrays.asList("Hello", "atguiu", "lambda", "ok", "www", "z");
List<String> res = filterStr(list, (str) -> str.length() > 2);
System.out.println(res);
}
//需求
public List<String> filterStr(List<String>list, Predicate<String>pre){
ArrayList<String>res = new ArrayList<>();
for(String str : list){
if(pre.test(str)){
res.add(str);
}
}
return res;
}
方法引用和構造器引用
方法引用
使用前提: Lambda
體中呼叫方法的引數列表和返回值型別,要和函式式介面中抽象方法的引數列表和返回值型別保持一致;
- 語法格式(一) 物件::例項方法名
public void test1(){
//普通寫法
PrintStream ps = System.out;
Consumer<String>con = (x) -> ps.println(x);
con.accept("hello !");
System.out.println("----------------------");
//簡寫
Consumer<String>con1 = ps::println;
con1.accept("hello ! ");
System.out.println("----------------------");
//更簡單的寫法
Consumer<String>con2 = System.out::println;
con2.accept("hello ! ");
}
注意,這樣寫的前提: Consumer
中的accept()
方法和println()
方法的引數列表和返回型別要完全一致:
再看一個例子:
三種寫法的效果是一樣的:
public class TestLambda {
public static void main(String[] args) {
// method 1
Consumer<String> consumer = s -> System.out.println(s);
useConsumer(consumer,"123");
//method 2
useConsumer(s -> System.out.println(s),"123");
//method3 method reference (方法引用)
useConsumer(System.out::println,"123"); //因為println和 accept 是同樣的只有一個入參,沒有返回值
}
public static <T> void useConsumer(Consumer<T> consumer,T t){
consumer.accept(t);
}
}
再看一個例子:
public static void main(String[] args) {
//都是輸出 字元 'l'
BiFunction<String,Integer,Character> bf = String::charAt; //這裡第一個必須傳入 String
Character c = bf.apply("hello,", 2);
System.out.println(c);
//注意這裡使用的是Function 介面
String str = new String("hello");
Function<Integer,Character> f = str::charAt; //這裡不需要String
Character c2 = f.apply(2);
System.out.println(c2);
}
再看一個例子:
public void test2(){
Employee emp = new Employee("zx",23,5555);
Supplier<String>sup = () -> emp.getName();
System.out.println(sup.get());
//簡寫
Supplier<String>sup2 = emp::getName;
System.out.println(sup2.get());
}
- 語法格式(二) 類名::靜態方法
public void test3(){
Comparator<Integer>com = (x,y) -> Integer.compare(x,y);
Comparator<Integer>com2 = Integer::compare;
}
Integer
類中的
Comparator
介面中的方法:
- 語法格式(三) 類::例項方法名
使用注意: 若Lambda引數列表中的第一個引數是例項方法的第一個呼叫者,而第二個引數是例項方法的引數時,可以使用ClassName :: method
。
public void test4(){
BiPredicate<String,String>bp = (x,y) -> x.equals(y);
BiPredicate<String,String>bp2 = String::equals;
}
構造器引用
需要呼叫構造器的引數列表,要與函式式介面中的抽象方法的引數列表保持一致;
public void test5(){
Supplier<Employee>sup = () -> new Employee();
Supplier<Employee>sup2 = Employee::new; //呼叫的是預設的
System.out.println(sup2.get());
}
輸出:
name='null', age=0, salary=0.0
再看構造器一個引數的:
public void test6(){
Function<String,Employee>fun = Employee::new;
System.out.println(fun.apply("zx"));
}
輸出:
name='zx', age=0, salary=0.0
如果想要匹配多個的,(兩個的可以使用BiFunction
),下面看一個三個的:
例如想匹配這個:
public class ComplexApple {
private String name;
private int weight;
private String color;
public ComplexApple() {
}
//匹配這個構造方法
public ComplexApple(String name, int weight, String color) {
this.name = name;
this.weight = weight;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
自己建一個介面:
@FunctionalInterface
public interface ThreeFunction<A,B,C,R> {
R apply(A a,B b,C c);
}
測試:
public class Test {
public static void main(String[] args) {
ThreeFunction<String,Integer,String,ComplexApple> tf = ComplexApple::new;
ComplexApple apple = tf.apply("藍色", 12, "好蘋果");
}
}
相關文章
- Java 8 Lambda 表示式學習心得總結Java
- Java 8新特性:lambda表示式(四)轉載總結Java
- lambda 表示式
- lambda表示式
- 禿頭星人進化記(Lambda表示式學習總結)
- Java | Lambda表示式Java
- Lambda表示式(Java)Java
- java lambda 表示式Java
- 八,Lambda表示式
- Java Lambda表示式Java
- [轉]Java 8 的 lambda 表示式 Java 8 的 lambda 表示式Java
- C# Lambda表示式詳解,及Lambda表示式樹的建立C#
- 淺談lambda表示式
- Lambda表示式詳解
- Java之lambda表示式Java
- kotlin lambda表示式Kotlin
- Python - lambda 表示式Python
- Java的Lambda表示式Java
- Java 8 Lambda 表示式Java
- C++Lambda表示式C++
- java 8 lambda表示式Java
- Java Lambda 表示式初探Java
- Python Lambda 表示式Python
- 【Kotlin】Lambda表示式Kotlin
- C#lambda表示式C#
- cpp的lambda表示式
- Java 基礎 —— Lambda 表示式Java
- Lambda 表示式的應用
- lambda表示式——快速入門
- c++之lambda表示式C++
- Java 中的 Lambda 表示式Java
- Java lambda表示式基本使用Java
- Java8-Lambda表示式Java
- java8 lambda表示式Java
- 「C++11」Lambda 表示式C++
- 掌握 Java 8 Lambda 表示式Java
- Java筆記:Lambda表示式Java筆記
- C#中的Lambda表示式和表示式樹C#