Java SE 8新功能介绍:Lambda的遍历,过滤,处理集合和方法增强
在“ Java SE 8新功能導覽”系列的這篇文章中,我們將深入解釋并探索代碼,以了解如何使用lambda表達式和方法引用 遍歷集合 ,并使用謂詞接口過濾它們,實現默認方法在接口中,最后在接口中實現靜態方法 。
在上一篇文章“ 使用Lambda Expression進行函數式編程 ”中; 我已深入了解lambda表達式。 我向您展示了Lambda表達式的幾種不同用法。 它們都有功能接口的共同實現。 我還解釋了編譯器如何從代碼中推斷信息,例如特定類型的變量以及后臺實際發生的情況。
源代碼托管在我的Github帳戶上:從此處克隆它。
表中的內容:
1,使用lambda表達式遍歷集合
在Java SE 8中,可以使用lambda表達式遍歷項目集合。 集合的示例包括列表,地圖和集合。 所有這些數據類型都實現了一個稱為iterable的接口。 為了理解我將要展示的代碼,讓我們從文檔開始。 我正在eg.com.tm.java8.features.lambda2包下的名為TraverseFileContent.java的類中工作。 我將右鍵單擊方法名稱readAllLines ,然后單擊 Show Javadoc。
它返回擴展了許多接口的List類。 這是我感興趣的一種叫做可迭代的。 此接口已在Java SE 5中添加。 它有一種稱為iterater的方法。 這將返回一個Iterater接口的實例,您可以使用該實例然后遍歷集合的內容。 但是在Java SE 8中,有兩種新方法。 一臺叫做forEach ,另一臺叫做spliterator 。 我將專注于forEach方法。
它接受稱為Consumer的功能接口的實例。 使用者接口只有一個抽象方法,名為accept 。 通過實現此接口及其單一抽象方法,您可以添加對集合中的某個項目進行操作的代碼。
因此,讓我們回到代碼。 在TraverseFileContent.java類中,我遍歷了這行集合,即表示文件內容的字符串數組列表,兩次。 在第一個版本中,從第51行開始,我使用了forEach循環,這是一段簡單的代碼,它為列表中的每個項目創建一個String變量行,然后執行所需的任何代碼。
在第58行,我使用了另一種方法,即調用集合的迭代器方法。 獲取迭代器對象,然后循環使用while代碼塊,調用迭代器具有next方法。 現在,這兩段代碼都可以很好地工作到Java 5,但我將向您展示使用lambda表達式和forEach方法的情況。 在Java 8中,我將從此forEach方法開始。
從65行開始,我將引用我的lines集合并調用新的forEach方法。 再一次,它將接收Consumer接口的實例。 因為Consumer是一個功能接口,所以我可以使用lambda表達式。 使用者接口的accept方法需要一個具有適當數據類型的參數。 因為lines變量被聲明為字符串列表,所以我必須傳入一個字符串作為參數。
我用行名將其傳遞。 然后,我將添加我的lambda運算符, 箭頭標記 。 我將在此處添加我的系統輸出,并直接傳遞。
您可以通過刪除舊的遍歷來清理代碼。 現在,這一行代碼取代了forEach循環的三行。 我將復制此代碼并將其移至使用迭代器的版本。 然后,我將選擇這些代碼行并將其注釋掉。 并粘貼我的lambda表達式版本。 因此,當您替換迭代器時,您將用一行替換四行代碼。
您可以將Stream與集合一起使用更高級,Streams支持一系列元素,這些元素支持順序和并行聚合操作。 通常,它用于以下用途:
現在保存并運行代碼,看看它的行為與以前完全相同,但是代碼更少。 因此,這只是遍歷集合的另一種選擇。 您可以使用forEach循環。 您可以使用迭代器對象,現在可以對lambda表達式使用forEach方法。
2.使用謂詞接口過濾集合
除了新的lambda語法外, Java SE 8還添加了許多新的功能接口 。 最有用的一種稱為Predicate接口。 該接口具有一個名為test布爾方法,您可以使用該方法包裝條件處理,并使條件代碼更加整潔。 我將向您展示如何在該項目中使用謂詞接口。
從包eg.com.tm.java8.features.lambda2.predicate名為FileDirFilter類eg.com.tm.java8.features.lambda2.predicate ,我聲明了一個名為predicateInInnerClass的方法。
我將使用NIO.2 DirectoryStream類來獲取特定的路徑條目流,在我們的案例中該流聲明為:
現在,我的目標是過濾此流,僅顯示某些條目,即目錄。 您可以將謂詞接口與內部類語法或lambda語法一起使用。
我將從內部類語法開始。 在predicateInInnerClass方法中,我將聲明謂詞接口的實例。 我將輸入接口名稱,然后按Ctrl + Space 。 然后從列表中選擇接口。 它是Java.util.function包的成員,在那里您Java.util.function找到許多其他新的功能接口。 該接口需要通用類型聲明。 我將其設置為Path類。 我將謂詞命名為dirsFilter 。
現在,對于內部類語法,我將從new關鍵字開始,然后按Ctrl + Space并為謂詞接口選擇構造函數。 選擇該選項后, NetBeans會自動實現單個抽象方法test 。 因為我用通用的Path類型聲明了謂詞,所以test方法也接受該類型的單個參數。 現在,我將實現該方法。 我將要傳遞的路徑對象的名稱更改為“ t”。
我將return語句設置為使用非常簡單的條件。 我將添加一組括號來包裝我的條件。 然后將條件設置為isDirectory(t, NOFOLLOW_LINKS) 。 因此,現在我的謂詞對象封裝了我的測試,并且可以使用測試方法來確定我是否要處理集合中的對象。
下一步是遍歷集合。 您可以通過forEach循環,迭代器對象或新的forEach方法以多種方式執行此操作。
我將使用經典的forEach循環。 我將輸入foreach并按Ctrl + Space ,然后選擇foreach代碼模板。 在for循環中,我將使用一條if語句。 我將設置條件以使用我剛剛聲明的dirsFilter對象。 我將呼叫dirsFilter.test 。 我將在foreach循環中傳遞要聲明的文件對象。 然后,如果條件為true,則將使用系統輸出,并將輸出調用file對象的getFileName方法的結果。
我將保存并運行該代碼。 而且我只看到目錄類型路徑的完整路徑。 現在,如果我想更改條件,則可以更改謂詞對象并重新運行代碼,它將可以正常工作。 但是我們這里的目標是使代碼盡可能簡潔和可讀。 為此,您可能決定使用lambda表達式來實現此謂詞接口。 因此,讓我們回到代碼,然后折疊該方法predicateInInnerClass ,并展開其他方法predicateWithLambda 。
現在,我將進行非常類似的處理。 但這次我將使用lambda表達式聲明我的謂詞對象。 再一次,我將輸入接口的名稱。 我將按Ctrl + Space并從列表中選擇它,然后設置其通用類型。 我將這個謂詞對象noFilter 。 現在,我將使用lambda表達式實現該接口。 我將從要實現的方法的簽名開始。 那是test方法。 并且由于我聲明的謂詞具有通用的Path類型,因此該參數將成為path的實例。
我將其命名為p 。 然后,我將添加arrow標記 ,然后使用一個簡單的條件表達式來實現我的方法,將其設置為true即可返回所有已定義的路徑條目。 這就是我所需要的。 在內部類版本中,這一行代碼替換了五到六行代碼。 接下來,我將遍歷該列表并使用謂詞對象的test方法。 這次,我將使用doFilterAndPrintPath方法傳入謂詞noFilter 。
我展示了如何在以前的lambda實現中使用此方法。 這是Java SE 8中添加的一個新方法。在doFilterAndPrintPath方法的實現中,我使用newDirectoryStream返回的路徑集合的forEach方法,我將從作為參數傳遞的對象的名稱開始。 這次,我不會將其包裝在括號內只是為了向您展示語法上的差異。 然后,我將添加箭頭標記和一對大括號。 在大括號內,我將使用if語句。 然后我將通過條件,這也是謂詞的test方法。 我將使用pred.test ,并傳遞path對象。
如果條件為真,我將使用系統輸出。 然后我將輸出路徑對象文件名。 我將保存并運行代碼。 結果就是。 我再次顯示所有條目。 但是,如果我想處理不止一種可能的情況怎么辦? 好吧,這是關于lambda語法和謂詞接口的偉大之處。
您可以根據需要創建任意多個謂詞對象,每個謂詞對象代表不同的條件。
因此,我將復制這一行代碼,并將新代碼的名稱更改為hiddenFilter 。 并且我將其條件更改為僅顯示隱藏的文件和目錄。 為了使傳遞想要的謂詞真的很容易,我將遍歷集合的這段代碼放入單獨的方法中。 我們已經在doFilterAndPrintPath方法中做到了。
現在,該方法不知道它將獲得哪個謂詞,因此我將方法中謂詞對象的名稱重構為pred 。 現在,我現有的代碼將傳遞給hiddenFilter ,所以我將運行它。 而且我得到了所有隱藏的文件和目錄 。 然后將傳入的謂詞對象更改為timeFilter ,然后再次運行代碼。
這次我得到了今天修改的所有文件和目錄。 因此,這就是您可以使用新的謂詞接口和lambda表達式將條件封裝在單個對象中的方式。 然后將這些對象傳遞到您自己的方法中進行處理。
我不會詳細介紹Java SE 8中的許多其他新功能接口,但是我鼓勵您研究一下謂詞接口是java.util.function的一部分的程序包。 您會在那里找到許多新的有用界面。
而且由于它們都是功能性接口,因此都可以使用lambda表達式實現。
3,使用方法引用遍歷集合
除了Lambda表達式外,Java SE 8的Project Lambda還向該語言添加了方法引用。 方法參考提供了一種命名您要調用的方法的方法,而不是直接調用它。 就像Lambda表達式一樣,目標是使您的代碼更簡潔,更易讀。
我將在名為MethodReference此類中的類( MethodReference下進行eg.com.tm.java8.features.lambda2.mthdRefs 。
您可以對四種方法使用方法引用:
我將從靜態方法開始。 在這段代碼中,我有一個FilesDirTests類,我的目標是檢查是否可以訪問特定文件。 我將創建一個對路徑類進行一些比較的方法。 現在您可以將此方法放置在任何您喜歡的位置,并且開發人員在最佳放置位置上會有所不同,但是我將創建該方法作為我的FilesDirTests類的靜態方法。
我將打開類,并放置此新方法。 我將其聲明為public和static ,并將其返回數據類型設置為boolean 。 我將方法命名為isAccessible 。 并且該方法將接受對路徑類的引用。 我將其命名為p。 該方法將知道目標是將路徑與java.nio.file.Files類中定義的某些可訪問性方法進行比較。 就像Predicate接口的test方法一樣,如果該路徑可訪問,則返回true,否則返回false表示該路徑不可訪問。
我將保存該更改,現在我將進入類MethodReference 。 為了測試給定路徑文件的可訪問性,我再次使用doFilterAndPrintPath(Predicate<Path> pred)方法。 我將在doPrint方法中調用它。 定義的路徑在doFilterAndPrintPath方法內部使用。 對于謂詞對象,我將使用方法reference 。 它看起來像這樣,我指的是靜態方法,因此我將從包含靜態方法的類的類型開始。
然后,我將輸入雙冒號運算符,這就是您將類型或對象與要調用的方法的名稱分開的方式。 然后,我將傳入方法的名稱isAccessible 。 現在,這就是為什么這樣做的原因。 此方法doFilterAndPrintPath方法期望謂詞接口的實例。 該接口只有一個抽象方法,該方法需要一個值。 我正在調用一個期望一個值的方法。 并返回測試方法可以使用的數據類型。
doFilterAndPrintPath將遍歷路徑文件,并根據測試輸出值。 保存更改,然后運行代碼,即可得到結果。 Opppps沒有結果? 沒有打印文件? 這是因為isAccessible具有測試條件,該條件會使測試失敗,這是isExecutable方法。
這是一個靜態方法參考。 如果愿意,可以將方法引用與實例方法一起使用 。 為此,我將在MethodReference類的結尾處添加以下兩個方法:
在此類的main方法中,我將創建當前類的實例,然后將其稱為doFilterAndPrintPath方法。 在我到這里時,所有數據和方法都是實例成員,而不是靜態成員。
因為main是靜態方法,所以我們不能使用this關鍵字,作為替代語法,您可以在對象實例方法中將此關鍵字用作引用。
和以前一樣,我將保存并運行,然后得到結果。 因此,方法引用是使代碼簡明扼要的一種簡單方法。
4,在接口中實現默認方法
在Java SE 8之前,接口可以包含抽象方法和常量聲明,但是您不能提供可繼承的完全實現的方法。
我正在使用一個名為eg.com.tm.java8.features.lambda2.defltMthd的軟件包。 在此應用程序中,我有一個名為VehicleInterface.Java的接口。 它有八個抽象方法,在接口中所有抽象方法都即將公開,因此我沒有包含public關鍵字,它們是基本的getter和setter 。
然后,我有一個名為Car.java的類,該類具有實現的setter和getter。 還有一個構造函數方法,可以很容易地實例化該類。
然后,我有一個名為DefaultMethod.java的主類。 在這段代碼中,我使用謂詞對象過濾汽車列表,然后顯示汽車。 我將一個名為info的字符串放在一起,并將其輸出到控制臺。 因此,我將使用Java SE 8的新功能來重構此代碼,該功能使我可以向接口添加稱為默認方法的內容。
將默認方法添加到接口時,可以添加其完整實現。 然后,實現該接口的任何類都將繼承該方法,并且您可以調用該方法本身,或者該方法將可在應用程序的任何其他位置調用,因為與抽象方法一樣,該方法將是公共的。
返回VehicleInterface.Java我將光標移到abstract方法下面。 然后,我將使用new關鍵字default開始方法簽名。 該方法的其余部分看起來完全一樣,就像我在一個類中實現它一樣。
我將從返回類型開始,然后是方法的名稱。 接下來,我將添加代碼,它將是一個return語句,可以將name , model ,car CC和make year的值連接起來。 現在, 因為這是一個接口,所以我不能引用私有字段。 你不能那樣做 。
因此,我將只引用抽象方法,我知道它將由類本身實現。 我將調用getName , getModel , getCC 。 然后,我將getMakeYear和getMakeYear圓括號連接getMakeYear 。
我將保存該更改,現在該方法可用于實現該接口的每個類。 我不會對Car類進行任何更改。 它已經有該方法。 然后,我將在這里轉到主類,使用默認方法,然后更改此代碼。 我不再需要創建名為Info的字符串,這將由Car類繼承的新方法完成。 因此,我將注釋掉那行代碼。 然后,我將使用對getInfo方法的調用來替換對info變量的引用。
但是我將其稱為我現在正在使用的汽車對象的成員。 我將保存更改并運行代碼。 結果就來了。 我成功地調用了getInfo方法來獲取字符串,名稱,模型,CC和制造年份的值的串聯,然后使用主類中的代碼將其輸出到控制臺。
通過使用默認方法 ,有時可以消除整個繼承層。 例如,某些開發人員在Java的早期版本中可能創建了一個接口,然后創建了實現該接口的基類,然后創建了他們將在其代碼中實際使用的子類。
使用此新功能,您可能根本不需要基類,而是可以直接實現子類,直接從接口繼承默認方法。
5,在接口中實現靜態方法
前面我已經描述了如何向接口添加默認方法,這些方法已完全實現并由實現類繼承。 在Java SE 8中,還可以向接口添加完全實現的靜態方法。 與默認方法一樣,目標是讓您消除繼承層并簡化應用程序。
我正在使用一個名為eg.com.tm.java8.features.lambda2.staticMthd的包。 就像在較早的項目中一樣,用于默認方法的那個主類在這里稱為StaticMethod ,它具有從汽車對象獲取名稱 , model , CC以及年份的代碼。
在本課程的第47行。 我的目標是采用此代碼并將其移至靜態方法,但不是將其添加到基類或其他具體類中,而是將其添加到接口中。 我在早期版本的Java中無法執行的操作。 我將使用此代碼并將其復制到剪貼板。 然后,我將打開名為VehicleInterface.java的接口。 和以前一樣,我從一組抽象方法聲明開始。 現在,將光標放在這些抽象方法聲明之后,并從關鍵字static開始。
與默認方法和抽象方法一樣,這將自動成為公共方法。 我不需要聲明它。 它將返回一個字符串,并將其命名為getVehicleInfo 。 現在,由于這是一個static方法,因此無法引用上面聲明的實例方法。 因此,我將傳遞Car對象的實例。 然后,我將提供return關鍵字,并粘貼之前的代碼,然后進行清理,以便現在返回name , model , CC以及make year和右括號。
現在,可以在應用程序中的任何位置使用此static方法。 和以前一樣,我無需對Model類做任何事情。 之所以稱為car ,是因為該接口中已經提供了要獲取字符串并輸出的所有代碼。
我將回到我的主類,使用靜態方法,現在,我將使用接口名稱VehicleInterface.java調用該接口,而不是在此處將這個字符串放在一起,然后將其稱為新的靜態方法, getVehicleInfo并傳入Car對象c 。
我將保存更改并運行代碼。 結果就來了。 從功能上講,它與使用默認方法,將此代碼放入Car類或在主類的頂級將其完全相同。
默認方法和靜態方法的目的只是為您提供更多選擇,以為您的應用程序組合繼承模型。 使用默認方法和靜態方法, 您可以消除應用程序的整個繼承層,并極大地簡化編碼模型,從而使應用程序更易于編碼和維護 。
資源資源
翻譯自: https://www.javacodegeeks.com/2014/12/java-se-8-new-features-tour-traversing-filtering-processing-collection-methods-enhancements-with-lambda.html
總結
以上是生活随笔為你收集整理的Java SE 8新功能介绍:Lambda的遍历,过滤,处理集合和方法增强的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果设置手写输入法在哪里
- 下一篇: 申请微信新账号