在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的項目,因此應該很容易直接導入和運行。