有没有办法在 Java 流上使用条件谓词?

Posted

技术标签:

【中文标题】有没有办法在 Java 流上使用条件谓词?【英文标题】:Is there a way to use conditional predicate on a Java stream? 【发布时间】:2021-10-22 03:36:36 【问题描述】:

我使用 Java 8,我有一个字符串列表,我想提取匹配(或不匹配)正则表达式的字符串:

switch (condition) 
    case "A":
         return myListOfString.stream().filter(myString -> myString.matches(myRegex)).findFirst();
    case "B":
         return myListOfString.stream().filter(myString -> !myString.matches(myRegex)).findFirst();

这两种情况下的代码完全相同,除了!“NOT”。我想根据条件提取匹配正则表达式的字符串或不匹配正则表达式的字符串。我确信可以在同一个流中使用 Predicate 执行此操作,但我不知道如何操作。

有没有一种方法可以做到这一点,而无需在一个流中复制代码?

【问题讨论】:

请阅读:Why is “Can someone help me?” not an actual question? docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/… 这里有Pattern::asPredicate可能对你有帮助 你正在使用一个开关,但把它当成是。 Predicate p = myString -> myString.matches(myRegex); 然后测试你的情况。 if( caseB ) p = Predicate.not(p); 然后流/过滤器返回。你不会有任何重复。 @Nono "我以前不知道 partitioningBy" 你知道有一个东西叫 Javadoc,对吧? 【参考方案1】:

如果我正确理解了您的问题,那么您想在不同条件下使用相同的流吗?

只需将开关盒移动到过滤器中即可。

return myListOfString.stream().filter(myString -> 

switch (condition) 
    case "A":
         return myString.matches(myRegex);
    case "B":
         return !myString.matches(myRegex));
    default: 
         return false;
    


).findFirst();

或者为过滤器创建一个谓词。

var myPredicate = (myString) -> 
switch (condition) 
    case "A":
         return myString.matches(myRegex);
    case "B":
         return !myString.matches(myRegex));
    default: 
         return false;
    
;

return myListOfString.stream().filter(myPredicate).findFirst();

【讨论】:

你传递filter(myPredicate),因为它已经是一个谓词。 您不能将varvar myPredicate = (myString) -> … 之类的lambda 表达式一起使用。【参考方案2】:

tl;博士

将您的输入分成两个列表,即通过谓词测试的列表和未通过测试的列表。在Map< Boolean , List< String > > 中收集这些列表。

List
.of( "dog" , "giraffe" , "cockatiel" , "cat" )
.stream()
.collect( 
    Collectors
    .partitioningBy( 
        word -> word.length() <= 3   // `Predicate` test. Looking for short words versus long words.
    ) 
)                                    // Returns a `Map< Boolean , List< String > >`.
.get(
    Boolean.TRUE
)                                    // Returns one of the two `List` objects contained in our map.
.toString()

[狗,猫]

Collectors.partitioningBy

作为commented by Abhijit Sarkar,您可以使用专门的Collector 创建一个映射,其中每个可能的Boolean 有两个键,每个值都是与您的谓词匹配或不匹配的输入列表。见Collectors.partitioningBy

Out 谓词正在测试包含三个或更少字符的短词:word -&gt; word.length() &lt;= 3

我们生成两个列表。短词(符合我们谓词测试的词)位于分配给Boolean.TRUE 键的列表中。长词(未通过谓词测试的词)位于分配给Boolean.FALSE 的列表中。

List< String > words = List.of( "dog" , "giraffe" , "cockatiel" , "cat" ) ;
Map< Boolean , List< String > > map = words.stream().collect( Collectors.partitioningBy( word -> word.length() <= 3 ) ) ;

转储到控制台。

System.out.println( "map = " + map ) ;
System.out.println( "short words = " + map.get( Boolean.TRUE ) ) ;

看到这个code run live at IdeOne.com。

map = false=[giraffe, cockatiel], true=[dog, cat]

短词=[狗,猫]

【讨论】:

当所有 OP 想要做的事情时,安静沉重是findFirst()...【参考方案3】:

好吧,在问题结束之前,答案是传递一个要使用的谓词。

Predicate<String> predicate;
switch (condition) 
    case "A":
        predicate = myString -> myString.matches(myRegex);
    case "B":
        predicate = myString -> !myString.matches(myRegex);


return myListOfString.stream().filter(predicate).findFirst();

注意,我没有尝试编译,但它应该可以让您了解要点。

【讨论】:

或者在最近的Java版本中,可以使用switch expression 保持简单。 您需要一个默认值,因为谓词可能尚未初始化。 从 Java 11 开始,使用 Predicate.not: condition.equals( "A" ) ? predicate : Predicate.not( predicate ) 使用filter(predicate::test) 而不仅仅是filter(predicate) 是没有意义的。而switch 需要default,否则会出现错误,“predicate 可能尚未初始化”

以上是关于有没有办法在 Java 流上使用条件谓词?的主要内容,如果未能解决你的问题,请参考以下文章

谓词下推

有没有办法通过 QueryDSL 中的谓词 API 急切地获取惰性关系?

std::map 有没有办法像键的谓词一样“编辑”值?

为啥“等待谓词”解决了条件变量的“丢失唤醒”?

coredata中谓词的使用

python条件变量`wait_for`谓词没有立即返回