2013年4月3日 星期三

Java 8 取出 Collection element 的方式

原文網址:http://www.javacodegeeks.com/2013/03/extracting-the-elements-of-the-java-collection-the-java-8-way.html

譯文中的 Collection,代表 Collection API 或是屬於 Collection 的 class(List、Map...)。 如果是 collection,則代表某個 Collection 的 instance。


我們都廣泛使用 Collection, 像是 ListMap 以及延伸的 class。 每次我們用的時候,我們都得掃遍整個 collection 去找到某些 element、 更新它們、或是找出某個條件下不同的 element。 就像下面這個 PersonList

List<Person> personList = new ArrayList<>();
personList.add(new Person("Virat", "Kohli",22));
personList.add(new Person("Arun", "Kumar",25));
personList.add(new Person("Rajesh", "Mohan", 32));
personList.add(new Person("Rahul", "Dravid", 35));

要找出 Person 的年齡大於 30 的 instance,我們會這樣作:

List<Person> olderThan30OldWay = new ArrayList<>();
for ( Person p : personList){
    if ( p.age >= 30){
        olderThan30OldWay.add(p);
    }
}
System.out.println(olderThan30OldWay);

就會得到這樣的輸出結果:

[Rajesh Mohan, 32, Rahul Dravid, 35]

程式碼是很好寫,但會不會有點囉唆、尤其是迴圈的部份? 為甚麼我們得要有迴圈呢? 如果有 API 可以掃描內容、然後給我們最終結果, 例如我們給一個 List、然後用一串 method 之後就可以取得我們想要的 List 結果? 有的,在 Scala、Groovy 這種有支援 closure、也支援內部迴圈的語言就有可能做到。 但是有給 Java 開發人員的解決方案嗎? 有的,這個問題在導入 Lambda Expression(closure) 後, 利用 lambda expression 來 加強 Collection API,就可以解決。 不過壞消息是,這會是 Java 8 的一部分, 需要一點時間才會變成主流的開發方式。

在上面的情境中使用 Java 8 的強化功能

正如我知前所說,Collection API 正在補強以支援 lambda expression, 你可以在這篇文章中了解更多內容。 JDK 團隊並不是把所有新的 API 加到 Collection 當中, 而是創造了一個新的概念 Stream, 並且把大部分的 API 加到那個 class 當中。 Stream 是 element 的序列,在建立時從 collection 取得。 要了解更多 Stream 的起源,請參考這篇文章。 要實作這個範例,我開始使用 Java 8 的強化功能、一些新的 API: stream()filter()collect()Collectior.toCollection

  • stream():collection 用這個 API 可以建立一個 Stream 的 instance。

  • filter():這個 method 接收一個會回傳 boolean 值的 lambda expression。 這個 lambda expression 會替換成 Predicate class 的實作。

  • collect():有兩個 overloaded 的版本。我在這邊用其中一個, 可以取得 Collector 的 instance。 這個 method 會取得 stream 的內容然後建立另一個 collection, 建立的邏輯在 Collector 當中定義。

  • Collectors.toCollection()CollectorsCollector 的 factory(pattern)。 toCollection() 需要一個可以回傳任何 Collection class instance 的 lambda expression / method reference。

簡單介紹要用的 API 之後,讓我來展示一下跟第一個範例等意的程式碼:

List<Person> olderThan30 = 
    //Create a Stream from the personList
    personList.stream().
    //filter the element to select only those with age >= 30
    filter(p -> p.age >= 30).
    //put those filtered elements into a new List.
    collect(Collectors.toCollection(() -> new ArrayList<Person>()));
System.out.println(olderThan30);

上面的程式碼使用內部迴圈以及 lambda expression 讓它看起來直覺、簡潔、還可以舒緩眼睛不適。 (譯註:我承認最後一個是惡搞亂翻的 [逃]) 如果你不熟悉 lambda expression 的想法, 可以看一下我之前寫的文章,裡頭有簡單的介紹。

沒有留言:

張貼留言