在Guava中過濾和轉換集合

1.概述

在本教程中,我們將說明如何使用Guava過濾和轉換集合

我們將使用謂詞進行過濾,使用庫提供的函數進行轉換,最後,我們將了解如何將過濾和轉換結合起來。

2.篩選集合

讓我們從過濾集合的簡單示例開始。我們將使用由庫提供並通過Predicates實用程序類構造的現成的Predicate:

@Test

 public void whenFilterWithIterables_thenFiltered() {

 List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

 Iterable<String> result

 = Iterables.filter(names, Predicates.containsPattern("a"));



 assertThat(result, containsInAnyOrder("Jane", "Adam"));

 }

如您所見,我們正在過濾名稱列表,以僅獲取包含字符“ a”的名稱-並且我們正在使用Iterables.filter()進行操作。

另外,我們也可以充分利用Collections2.filter() API:

@Test

 public void whenFilterWithCollections2_thenFiltered() {

 List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

 Collection<String> result

 = Collections2.filter(names, Predicates.containsPattern("a"));



 assertEquals(2, result.size());

 assertThat(result, containsInAnyOrder("Jane", "Adam"));



 result.add("anna");

 assertEquals(5, names.size());

 }

這裡需要注意的幾件事-首先, Collections.filter()的輸出是原始集合的實時視圖-對其中一個的更改將反映在另一個中。

同樣重要的是要了解,現在,結果受謂詞約束-如果我們添加不滿足該謂詞的元素,則將拋出IllegalArgumentException

@Test(expected = IllegalArgumentException.class)

 public void givenFilteredCollection_whenAddingInvalidElement_thenException() {

 List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

 Collection<String> result

 = Collections2.filter(names, Predicates.containsPattern("a"));



 result.add("elvis");

 }

3.編寫自定義過濾器謂詞

下一步–讓我們編寫我們自己的謂詞,而不使用庫提供的謂詞。在下面的示例中,我們將定義一個僅獲取以“ A”或“ J”開頭的名稱的謂詞:

@Test

 public void whenFilterCollectionWithCustomPredicate_thenFiltered() {

 Predicate<String> predicate = new Predicate<String>() {

 @Override

 public boolean apply(String input) {

 return input.startsWith("A") || input.startsWith("J");

 }

 };



 List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

 Collection<String> result = Collections2.filter(names, predicate);



 assertEquals(3, result.size());

 assertThat(result, containsInAnyOrder("John", "Jane", "Adam"));

 }

4.合併多個謂詞

我們可以使用Predicates.or()Predicates.and()組合多個謂詞。
在以下示例中–我們過濾名稱列表以獲取以“ J”開頭或不包含“ a”的名稱:

@Test

 public void whenFilterUsingMultiplePredicates_thenFiltered() {

 List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

 Collection<String> result = Collections2.filter(names,

 Predicates.or(Predicates.containsPattern("J"),

 Predicates.not(Predicates.containsPattern("a"))));



 assertEquals(3, result.size());

 assertThat(result, containsInAnyOrder("John", "Jane", "Tom"));

 }

5.在過濾集合時刪除空值

我們可以使用Predicates.notNull()過濾集合中的值,如以下示例所示:

@Test

 public void whenRemoveNullFromCollection_thenRemoved() {

 List<String> names =

 Lists.newArrayList("John", null, "Jane", null, "Adam", "Tom");

 Collection<String> result =

 Collections2.filter(names, Predicates.notNull());



 assertEquals(4, result.size());

 assertThat(result, containsInAnyOrder("John", "Jane", "Adam", "Tom"));

 }

6.檢查集合中的所有元素是否都符合條件

接下來,讓我們檢查集合中的所有元素是否都符合特定條件。我們將使用Iterables.all()檢查所有名稱是否都包含“ n”或“ m”,然後我們將檢查所有元素是否均包含“ a”:

@Test

 public void whenCheckingIfAllElementsMatchACondition_thenCorrect() {

 List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");



 boolean result = Iterables.all(names, Predicates.containsPattern("n|m"));

 assertTrue(result);



 result = Iterables.all(names, Predicates.containsPattern("a"));

 assertFalse(result);

 }

7.轉換集合

現在–讓我們看看如何使用Guava Function轉換集合。在下面的例子-我們改變名稱的列表,以整數(名字的長度)與Iterables.transform()列表

@Test

 public void whenTransformWithIterables_thenTransformed() {

 Function<String, Integer> function = new Function<String, Integer>() {

 @Override

 public Integer apply(String input) {

 return input.length();

 }

 };



 List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

 Iterable<Integer> result = Iterables.transform(names, function);



 assertThat(result, contains(4, 4, 4, 3));

 }

我們還可以使用Collections2.transform() API,如以下示例所示:

@Test

 public void whenTransformWithCollections2_thenTransformed() {

 Function<String,Integer> func = new Function<String,Integer>(){

 @Override

 public Integer apply(String input) {

 return input.length();

 }

 };



 List<String> names =

 Lists.newArrayList("John", "Jane", "Adam", "Tom");

 Collection<Integer> result = Collections2.transform(names, func);



 assertEquals(4, result.size());

 assertThat(result, contains(4, 4, 4, 3));



 result.remove(3);

 assertEquals(3, names.size());

 }

請注意, Collections.transform()的輸出是原始Collection的實時視圖-更改一個會影響另一個。

並且–與之前相同–如果我們嘗試將元素添加到輸出Collection中,則會引發UnsupportedOperationException

8.從謂詞創建函數

我們還可以使用Functions.fromPredicate()謂詞創建Function 。當然,這將是一個根據謂詞條件將輸入轉換為Boolean的函數。

在下面的例子中,我們把名字的清單,其中每個元素表示如果名稱中包含“M”布爾值的列表:

@Test

 public void whenCreatingAFunctionFromAPredicate_thenCorrect() {

 List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

 Collection<Boolean> result =

 Collections2.transform(names,

 Functions.forPredicate(Predicates.containsPattern("m")));



 assertEquals(4, result.size());

 assertThat(result, contains(false, false, true, true));

 }

9.兩個職能的組成

接下來–讓我們看一下如何使用組合Function轉換Collection。

當其應用於所述第二功能在所述第一函數的輸出Functions.compose()返回兩種功能的組合物。

在以下示例中–第一個Function將名稱轉換為長度,然後第二個Function將長度轉換為布爾值,該布爾值表示名稱的長度是否為偶數:

@Test

 public void whenTransformingUsingComposedFunction_thenTransformed() {

 Function<String,Integer> f1 = new Function<String,Integer>(){

 @Override

 public Integer apply(String input) {

 return input.length();

 }

 };



 Function<Integer,Boolean> f2 = new Function<Integer,Boolean>(){

 @Override

 public Boolean apply(Integer input) {

 return input % 2 == 0;

 }

 };



 List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

 Collection<Boolean> result =

 Collections2.transform(names, Functions.compose(f2, f1));



 assertEquals(4, result.size());

 assertThat(result, contains(true, true, true, false));

 }

10.結合過濾和轉換

現在,讓我們看一下Guava擁有的另一個很酷的API,它實際上將允許我們將過濾和轉換鏈接在一起,即FluentIterable

在以下示例中–我們過濾名稱列表,然後使用FluentIterable對其進行轉換

@Test

 public void whenFilteringAndTransformingCollection_thenCorrect() {

 Predicate<String> predicate = new Predicate<String>() {

 @Override

 public boolean apply(String input) {

 return input.startsWith("A") || input.startsWith("T");

 }

 };



 Function<String, Integer> func = new Function<String,Integer>(){

 @Override

 public Integer apply(String input) {

 return input.length();

 }

 };



 List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

 Collection<Integer> result = FluentIterable.from(names)

 .filter(predicate)

 .transform(func)

 .toList();



 assertEquals(2, result.size());

 assertThat(result, containsInAnyOrder(4, 3));

 }

值得一提的是,在某些情況下,命令性版本更具可讀性,應首選功能性方法。

11.結論

最後,我們學習瞭如何使用Guava過濾和轉換集合。我們使用Collections2.filter()Iterables.filter() API進行過濾,以及使用Collections2.transform()Iterables.transform()來轉換集合。

最後,我們快速瀏覽了非常有趣的FluentIterable fluent API,它結合了過濾和轉換功能。

所有這些示例和代碼段的實現都可以在GitHub項目中找到–這是一個基於Maven的項目,因此應該很容易直接導入和運行。