Java 8謂詞鏈

banq發表於2018-12-21

在本快速教程中,我們將討論在Java 8中謂詞鏈Predicates的不同方法。
讓我們看看如何使用簡單的謂詞來過濾名稱列表:

@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")
但是,如果我們想要應用多個謂詞呢?

多個過濾器
如果我們想要應用多個謂詞,一個選項是簡單地連結多個過濾器:

@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"));
}


複雜的謂詞
我們可以使用一個帶有複雜謂詞的過濾器,而不是使用多個過濾器:

@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"));
}


這個選項比第一個選項更靈活,因為我們可以使用按位運算來構建我們想要的複雜謂詞。

.結合  謂詞
如果我們不想使用按位運算構建複雜的謂詞,Java 8 Predicate可以使用有用的方法來組合謂詞。
我們將使用Predicate.and(),Predicate.or()和Predicate.negate()方法組合謂詞。

我們將明確定義我們的謂詞,然後我們將使用Predicate.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"));
}

我們可以看到,語法非常直觀,方法名稱表明了操作的型別, 

我們也可以使用  Predicate.or()來組合Predicates。
讓我們提取名稱以“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"));
}


在組合我們的Predicates時我們也可以使用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 來過濾List。

結合謂詞內聯
我們不需要明確定義 Predicates要使用and(),  or(), 和negate().
可以透過強制Cast謂詞來內聯它們  :

@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"));
}


結合一組謂詞
最後,讓我們看看如何透過Reducing合併它們來連結一組Predicates。
在下面的例子中,我們有一個列表的謂詞,我們使用組合Predicate.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 標識

如果我們想要使用Predicate.or()組合它們會有所不同:

@Test
public void whenFilterListWithCollectionOfPredicatesUsingOr_thenSuccess(){
    List<String> result = names.stream()
      .filter(allPredicates.stream().reduce(x->false, Predicate::or))
      .collect(Collectors.toList());
     
    assertEquals(2, result.size());
    assertThat(result, contains("Adam","Alexander"));
}



完整的原始碼可以在GitHub上獲得
 

相關文章