如何从 Java 中的自定义谓词列表中创建一个谓词?

Posted

技术标签:

【中文标题】如何从 Java 中的自定义谓词列表中创建一个谓词?【英文标题】:How to make a Predicate from a custom list of Predicates in Java? 【发布时间】:2017-10-27 21:04:34 【问题描述】:

我对编程比较陌生,过去两天我一直在想如何制作一个由其他谓词的自定义列表组成的谓词。所以我想出了某种解决方案。下面是一个代码 sn-p 应该给你一个想法。因为我只是根据阅读各种文档来编写它,所以我有两个问题:1/这是一个好的解决方案吗? 2/对于这个问题还有其他推荐的解决方案吗?

public class Tester 
  private static ArrayList<Predicate<String>> testerList;

  //some Predicates of type String here...

  public static void addPredicate(Predicate<String> newPredicate) 
    if (testerList == null) 
                 testerList = new ArrayList<Predicate<String>>();
    testerList.add(newPredicate);
  

  public static Predicate<String> customTesters () 
    return s -> testerList.stream().allMatch(t -> t.test(s));

  

【问题讨论】:

逻辑是对的,但设计很糟糕。为什么不只使用一个以 List> 作为参数并返回聚合谓词的方法?使用可变的静态字段真的很难看,并且使 Tester 类无法使用。 @JBNizet 你是对的 - 它更容易。而且更漂亮。我对设计还不太了解。 【参考方案1】:

Java Predicate 有一个很好的 AND 函数,它返回新的 Predicate,它是对两个谓词的评估。你可以用这个把它们全部合二为一。

https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html#and-java.util.function.Predicate-

例子:

Predicate<String> a = str -> str != null;
Predicate<String> b = str -> str.length() != 0;
Predicate<String> c = a.and(b);

c.test("Str");
//stupid test but you see the idea :)

【讨论】:

【参考方案2】:

你可以有一个静态方法来接收许多谓词并返回你想要的谓词:

public static <T> Predicate<T> and(Predicate<T>... predicates) 
    // TODO Handle case when argument is null or empty or has only one element
    return s -> Arrays.stream(predicates).allMatch(t -> t.test(s));

一个变种:

public static <T> Predicate<T> and(Predicate<T>... predicates) 
    // TODO Handle case when argument is null or empty or has only one element
    return Arrays.stream(predicates).reduce(t -> true, Predicate::and);

这里我使用Stream.reduce,它将身份和运算符作为参数。 Stream.reducePredicate::and 运算符应用于流的所有元素以生成结果谓词,并使用标识对流的第一个元素进行操作。这就是我使用t -&gt; true 作为标识的原因,否则结果谓词可能最终评估为false

用法:

Predicate<String> predicate = and(s -> s.startsWith("a"), s -> s.length() > 4);

【讨论】:

@JanMariaProkop 归约是通过运算符将许多元素转换为一个(或几个)的过程。典型示例是数字列表和求和运算符,即如果您有列表[1,2,3,4]+ 运算符(sum),则减少列表返回1+2+3+4 = 10。运算符获取列表的 2 个值并返回一个新值。逐步减少:(((1+2)+3)+4),所以第一步产生1+2=3,第二步产生(3)+3=6,第三步产生(6)+4=10 @JanMariaProkop 与您的谓词列表相同:该方法接收谓词列表,您正在应用运算符AND 将列表减少为一个最终谓词,然后返回。 Stream.reduce 方法的那个版本的细节是它接受标识,因为谓词流可以是空的,所以在这种情况下返回标识。身份是针对操作员定义的。这意味着identity OP element = element 用于流的所有元素。对于数字和运算符+,标识为0 @JanMariaProkop 对于谓词和运算符AND,标识是一个始终计算为true 的谓词。如果在任何谓词P(返回truefalse)和始终返回true 的谓词之间应用运算符AND,则结果将是P 之一,由于AND真值表:P(false) AND true = false // P(true) AND true = true。希望现在更清楚了。这是Stream.reduce 方法的链接。 @FedericoPeraltaSchaffner 现在真的很清楚了。我深深地感激它!我会点击某种 +1 按钮,但似乎没有。 @JanMariaProkop 您可以对答案进行投票,除了将其标记为正确,但您需要至少有 15 个声望点。

以上是关于如何从 Java 中的自定义谓词列表中创建一个谓词?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 stream().reduce() 基于谓词从一个列表中创建 2 个列表 [重复]

如何对实体的自定义属性进行谓词

从用户的角度使用 predicateEditor 定义子谓词的最佳方法

Prolog - 在将每个值加倍后,从原始列表中创建元素的新列表?

Prolog 中的子列表谓词

如何基于谓词实现列表拆分器