天天看點

Java 8 謂詞(Predicate)鍊Java 8 謂詞(Predicate)鍊

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方法中使用複雜謂詞或組合謂詞。