Java 8 謂詞(Predicate)鍊
本文介紹多種方式實作謂詞連結。
1. 從示例開始
我們先從一個簡單示例開始,如何實用簡單謂詞過濾集合:
@Test
public void whenFilterList_thenSuccess(){
List<String> names = Arrays.asList("Adam", "Alexander", "John", "Tom");
List<String> result = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
assertEquals(2, result.size());
assertThat(result, contains("Adam","Alexander"));
}
上面示例過濾名稱清單,僅保留以字母A開頭的名稱:
name -> name.startsWith("A")
那麼如何實作多個謂詞組合條件過濾?
2. 多條件過濾
2.1 直接連結調用
最簡單方法時直接連結調用:
@Test
public void whenFilterListWithMultipleFilters_thenSuccess(){
List<String> result = names.stream()
.filter(name -> name.startsWith("A"))
.filter(name -> name.length() < 5)
.collect(Collectors.toList());
assertEquals(1, result.size());
assertThat(result, contains("Adam"));
}
本例更新了上面的示例,同時過濾以A開投的名稱和長度小于5的名稱,我們實用兩個filter,每個過濾實用謂詞作為條件。
2.2 複雜的謂詞
我們可以修改上節示例,使用單個帶複雜謂詞參數的過濾器:
@Test
public void whenFilterListWithComplexPredicate_thenSuccess(){
List<String> result = names.stream()
.filter(name -> name.startsWith("A") && name.length() < 5)
.collect(Collectors.toList());
assertEquals(1, result.size());
assertThat(result, contains("Adam"));
}
這種方式更靈活,可以使用位操作符建構複雜的謂詞邏輯。
3. 組合謂詞
如果我們不想使用位操作符建構複雜的謂詞條件,Java 8 Predicate提供了有用的方法可以組合謂詞。常用的方法包括
Predicate.and(), Predicate.or(), and Predicate.negate()
。
3.1 Predicate.and()
下面示例首先定義兩個謂詞,然後使用and方法進行組合:
@Test
public void whenFilterListWithCombinedPredicatesUsingAnd_thenSuccess(){
Predicate<String> predicate1 = str -> str.startsWith("A");
Predicate<String> predicate2 = str -> str.length() < 5;
List<String> result = names.stream()
.filter(predicate1.and(predicate2))
.collect(Collectors.toList());
assertEquals(1, result.size());
assertThat(result, contains("Adam"));
}
我們看到文法相對直接,方法名表明了操作的類型。通過使用and(),我們實作了對清單過濾,僅傳回滿足兩個條件的名稱。
3.2 Predicate.or()
我們也可以使用or方法組合謂詞,下面示例同時傳回以字母J開頭或長度小于4的名稱:
@Test
public void whenFilterListWithCombinedPredicatesUsingOr_thenSuccess(){
Predicate<String> predicate1 = str -> str.startsWith("J");
Predicate<String> predicate2 = str -> str.length() < 4;
List<String> result = names.stream()
.filter(predicate1.or(predicate2))
.collect(Collectors.toList());
assertEquals(2, result.size());
assertThat(result, contains("John","Tom"));
}
3.3 Predicate.negate()
還有Predicate.negate()方法用于組合謂詞:
@Test
public void whenFilterListWithCombinedPredicatesUsingOrAndNegate_thenSuccess(){
Predicate<String> predicate1 = str -> str.startsWith("J");
Predicate<String> predicate2 = str -> str.length() < 4;
List<String> result = names.stream()
.filter(predicate1.or(predicate2.negate()))
.collect(Collectors.toList());
assertEquals(3, result.size());
assertThat(result, contains("Adam","Alexander","John"));
}
這裡使用 or 和 negate 方法組合過濾名稱以J開頭或者名稱長度不小于4的名稱集合。
3.4 通過lambda表達式組合謂詞
我們并不需要顯示定義謂詞才能組合,可以直接通過lambda表達式實作:
@Test
public void whenFilterListWithCombinedPredicatesInline_thenSuccess(){
List<String> result = names.stream()
.filter( ((Predicate<String>)name -> name.startsWith("A"))
.and(name -> name.length()<5) )
.collect(Collectors.toList());
assertEquals(1, result.size());
assertThat(result, contains("Adam"));
}
3.5 組合謂詞集合
最後我們看看如何連結謂詞集合。下面示例中定義了謂詞集合,然後通過reduce方法進行組合,符合條件使用and進行連結:
@Test
public void whenFilterListWithCollectionOfPredicatesUsingAnd_thenSuccess(){
List<Predicate<String>> allPredicates = new ArrayList<Predicate<String>>();
allPredicates.add(str -> str.startsWith("A"));
allPredicates.add(str -> str.contains("d"));
allPredicates.add(str -> str.length() > 4);
List<String> result = names.stream()
.filter(allPredicates.stream().reduce(x->true, Predicate::and))
.collect(Collectors.toList());
assertEquals(1, result.size());
assertThat(result, contains("Alexander"));
}
注意我們使用合并條件為:
x->true
。
但如果使用or()方法将它們組合在一起,情況就不同了:
@Test
public void whenFilterListWithCollectionOfPredicatesUsingOr_thenSuccess(){
List result = names.stream()
.filter(allPredicates.stream().reduce(x->false, Predicate::or))
.collect(Collectors.toList());
assertEquals(2, result.size());
assertThat(result, contains("Adam","Alexander"));
}
4. 總結
本文我們介紹了Java 8 中不同方式實作多謂詞組合過濾,可以在filter方法中使用複雜謂詞或組合謂詞。