Skip to main content
 首页 » 编程设计

Java 谓词(Predicate)链

2022年07月19日212dudu

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方法中使用复杂谓词或组合谓词。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/108519605
阅读延展