Lambda表达式Java教程
在本文中,我們提供了全面的Lambda Expressions Java教程。
1. Lambda Expressions Java教程–簡介
Lambda表達式被認為是Java 8中引入的最好的功能之一。Lambda表達式被認為是Java進入函數式編程世界的第一步 。 可以將其視為無需類即可創建的函數。 它也可以像參數一樣傳遞,并且可以在需要時和根據需要執行。 Java Lambda表達式是匿名函數的簡潔表示,可以將其傳遞。 具有單個功能的匿名類通過額外的語法呈現出笨拙的外觀。 這些表述旨在消除這種混亂。
如前所述,Java Lambda表達式是無名函數 ,可以作為常量值進行傳遞。 這意味著它們可以存在于可能存在任何其他常數值的任何位置,但是通常作為參數寫入某些其他函數。 考慮一個典型的例子,我們可以將比較函數傳遞給泛型排序函數,而不是麻煩地定義一個整個過程(并引起詞法不連續和名稱空間污染)來描述這種比較,我們只需傳遞一個lambda表達式描述了比較。 讓我們看一下Lambda表達式的一些屬性 :
- 匿名:它仍然可以稱為匿名,因為它沒有明確的名稱。
- 簡潔:正如前面提到的匿名類的情況,與匿名類相比,我們用Lambdas編寫的代碼要少得多。
- 函數:Lambda更像是函數而不是方法。 這是因為方法屬于類,而Lambda不屬于。 但是就像方法一樣,Lambda接受參數列表,具有主體并且還可以引發異常。
- 可以傳遞:Lambda可以傳遞給其他函數,就像普通參數一樣。
為了消除由于我們上面提到的觀點而引起的任何誤解,lambda不會添加引入之前的更多功能。 它只是改善了我們編寫代碼的方式,并減少了很多樣板代碼。 該樣板代碼甚至與我們用來通過基礎操作系統的多核性質進行代碼識別的系統級編程有關。 讓我們看一下這種簡單的語法糖如何使我們的工作在并行性,代碼簡潔性和緊湊性方面更容易。
2.編寫Lambda表達式
在本節中,我們將看到Java Lambda表達式如何減少執行一些簡單操作所需編寫的代碼行。 例如,我們將比較代碼行數以構成一個Comparator函數。 為了進行比較,我們將在此處創建一個簡單的POJO類,即Student類,其中包含Student ID(作為Long和name作為String參數:
學生.java
public class Student { ??private Long id; private String name; // standard setters and getters }比較我們在應用程序中定義的POJO對象是一種非常通用的編程實踐。 如果我們要比較兩個Student類對象,則可以使Comparator像這樣:
匿名類的比較器
Comparator<Student> byId = new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { return s1.getId().compareTo(s2.getId()); } };這是一個作為Anonymous類的簡單Comparator實現,但是當使用Lambda完成時,我們會發現相同的實現非常精確和干凈。 讓我們在這里看到使用Lambda表達式完成的相同任務:
pom.xml
Comparator<Student> byId = (s1, s2) -> s1.getId().compareTo(s2.getId());Lambda表達式上方也可以稱為塊Lambda表達式,因為它由>符號右側的單個代碼塊組成。 它變得更加簡潔小巧,這聽起來很神奇,請參見以下代碼片段:
Lambda的簡潔實現
Comparator<Student> byId = Comparator.comparing(Student::getId);這是建立比較器的好方法,而且也很簡單。 對于上面我們進行的Block Lambda表達式,讓我們將其分為幾部分以更好地理解:
Lambda表達- Lambda Expression以在此情況下傳遞給函數Comparator的參數列表開頭
- 箭頭符號將Lambda Expression參數與Lambda主體分開
- 主體清楚地將兩個學生對象及其id進行比較,該表達式定義了Lambda返回值
需要注意的是,編譯后的代碼(即匿名類版本和Lambda表達式版本的字節碼)將完全相同,因為Lambda表達式僅僅是使代碼清晰的語法。 盡管使用Lambda表達式有時可能會使代碼的可讀性降低。
3. Lambda表達式與匿名類
我們使用Lambda表達式編寫的代碼也可以使用Anonymous類編寫,其實現方式與Lambda Expressions完全相同。 區別在于Lambda代碼的簡潔性。
作為比較示例,讓我們構造一個類和一個將Runnable作為輸入的方法:
可運行類
public class RunnableInstance { public static void doSomething(Runnable runnable){ runnable.run(); } }當我們使用Anonymous類制作Runnable時,其外觀如下所示:
可通過匿名類運行
Runnable runnable = new Runnable() { @Override public void run() { System.out.print( "Anonymous class implementation." ); } }; doSomething(runnable);讓我們嘗試將上面的代碼轉換為Lambda表達式,看看如何得到干凈的東西:
可與Lambda一起運行
Runnable runnable = () -> System.out.print( "Lambda Expression." ); doSomething(runnable);如果我們不想多次使用可運行的實現,我們甚至可以避免進行引用:
簡潔的Lambda Runnable
doSomething(() -> System.out.print( "Lambda Expression." ));4.使用Lambda表達式進行并行編程
每當我們談論線程時,我們大多數人都會退后一步,考慮是否真的需要在我們的應用程序中實現線程以支持并行性,因為并行性本質上微不足道且難以管理。 當我們有一個項目集合時,我們實現了一個lambda,如:
并行編程
collection.map { // my lambda }在這里,集合本身能夠與提供的Lambda實現并行性,而不必自己實現線程。 這意味著,在多核環境中,Lambda可以在集合上進行流式傳輸時利用多個核。 就像我們考慮一個簡單的例子一樣:
Lambda與并行流
List<String> names = students.stream() .map(s -> s.getName().toUpperCase()) .collect(Collectors.toList());map函數可以在多核環境中并行運行,以一次處理多個對象,而無需我們做任何事情。 為此,僅需要執行此程序的操作系統必須是多核。 一旦滿足此條件,我們可以確保可以在給定語句中并行執行的任何操作都將自動完成。
5.收藏和流
Collections框架是Java中最常用的Framework API之一。 集合允許我們將相似的對象收集到可以針對特定目的進行優化的數據結構中。 前面的所有示例都需要對象的集合,因此,假設我們有一個Student類型的對象的集合,就像我們前面定義的那樣:
學生集合
List students = getStudentObjectCollection();我們從添加到Collection接口的新方法stream()開始。 由于所有集合都“擴展”集合,因此所有Java集合都繼承了此方法:
學生流
List students = getStudentObjectCollection(); Stream stream = students.stream(); // a stream of student objects盡管看起來很像,但Stream接口不是另一種常規的集合類型。 我們可以將Stream視為“數據流”抽象,它使我們能夠轉換或操縱其包含的數據。 與我們在Java中研究過的其他集合不同,Stream不允許我們直接訪問其包含的元素。 盡管如果您想訪問元素,我們總是可以將流轉換為Java中的集合之一并實現我們的目的。
出于演示目的,我們將看到如果我們必須計算students集合中有多少個奇數ID對象,我們的代碼將是什么樣子。 首先,讓我們看看如何在不使用流的情況下完成此操作:
計數奇數
long count = 0 ; List students = getStudentObjectCollection(); for (Student s : students) { if (s.getId() % 2 == 1 ) { count++; } }使用for循環,我們創建了一個計數器,每次在學生列表中遇到奇數ID時,該計數器都會遞增。 對于一個非常簡單的任務,我們已經數百次編寫了這種類型的代碼,它跨越多行。
我們也可以在一行中使用Stream編寫完全相同的代碼:
使用流
List students = getStudentObjectCollection(); long count = students.stream().filter(student -> student.getId() % 2 == 1 ).count();這看起來比以前的for循環方法干凈整潔嗎? 一切都始于調用stream()方法,該方法將給定的集合轉換為Stream,其他所有調用都鏈接在一起,因為Stream接口中的大多數方法都是在考慮Builder模式的情況下設計的 。 對于那些不習慣使用這種方法進行鏈接的用戶,可能更容易這樣可視化:
可視化流
List students = getStudentObjectCollection(); Stream stream = students.stream(); stream = stream.filter(student -> student.getId() % 2 == 1 ); long count = stream.count();讓我們將注意力集中在我們使用的Stream的兩種方法中, filter()和count() 。
filter()方法采用要過濾集合的條件,該條件由帶一個參數并返回布爾值的lambda表達式表示:
Lambda條件
student -> student.getId() % 2 == 1并非偶然,用于表示該表達式的功能接口filter()方法的參數filter()是謂詞接口。 它只有一個抽象方法boolean test(T t) :
功能介面
@FunctionalInterface public interface Predicate { boolean test(T t); // non-abstract methods here }參數化類型T表示流中元素的類型,即Student對象。 過濾之后,剩下的就是調用count()方法。 沒什么大不了的,它只是計算過濾發生后我們流中還剩下多少個對象(除了過濾之外,我們還可以有更多的東西)。 count()方法被視為“終端操作”,在調用該方法后,該流被稱為“已消耗”且無法再使用。
6. Lambda表達式的缺點
盡管帶有Lambda Expressions的代碼看起來非常簡潔,但是Lambdas也有一些缺點。 讓我們在這里研究其中的一些:
- 無法處理檢查的異常 :引發檢查的異常的任何代碼都應包裝在try-catch語句中。 但是,即使我們這樣做,也不總是總是清楚拋出的異常發生了什么。
- 性能問題 :由于JIT不能始終將forEach() + lambda優化到與普通循環相同的程度,因此Lambda可以在很小程度上影響性能。
- 調試挑戰 :顯然,使用Lambdas時,代碼并不總是那么簡潔。 這使得在代碼和可讀性方面發生的異常的堆棧跟蹤變得有些困難。
盡管Lambda有一些缺點,但是當您編寫簡潔的代碼時,它們仍然是一個很好的伴侶。
7.結論
Java Lambda表達式在所有LISP,Perl,Python以及最新版本的C ++,Objective C,C#和Java 8中都出現(具有不同的語法),但值得注意的是,即使它可以處理傳遞的函數(或一些借口)作為參數。 它們是具有特定語義的語法元素,并且這些語義對運行時的要求比C所設計的要高。
在本課中 ,我們可以閱讀有關Lambda表達式的更多信息,它與功能接口有很深的聯系,還演示了將并行流與Lambda表達式配合使用的性能比較,并加深了對Lambda表達式如何與功能接口一起使用并可以在簡單語句中使用的理解。利用多核操作系統提供的并行性,而無需了解幕后工作的API。
上次更新時間為2020年2月17日
翻譯自: https://www.javacodegeeks.com/lambda-expressions-java-tutorial.html
總結
以上是生活随笔為你收集整理的Lambda表达式Java教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 淘宝问问 AI 助手测试版上线:无需申请
- 下一篇: 为特使建立控制平面的指南-识别组件