当前位置: 首页 > 知识库问答 >
问题:

Java流:查找多个过滤器谓词的第一个

司徒啸
2023-03-14

我收集了< code >

             | isNew | isSoldOut
--------------------------------
predicate 1: | false | false
predicate 2: | false | true
predicate 3: | true  | false
predicate 4: | true  | true

我想找到“每种类型中的一种”,尽管我想找到产品集合中每个谓词的第一个匹配项。

目前我的代码如下:

java prettyprint-override">List<Product> products = getProducts();
List<Predicate<Product>> predicates = getPredicates();

List<Product> result = predicates.stream()
  .flatMap(predicate -> products.stream().filter(predicate).findFirst().stream())
  .collect(Collectors.toList());

但这当然会多次迭代产品集合,这是不需要的,因为在我的例子中,我有100000个产品和64个谓词,并且需要很长时间。

在我的特例中,谓词是互斥的:如果一个谓词返回true,那么对于该特定产品,所有其他谓词都可以跳过。因为我使用了< code>findFirst,所以对于所有其他产品,可以跳过这个谓词。

我想知道是否可以迭代产品集合,并根据所有谓词检查每个产品一次。

共有3个答案

龙弘盛
2023-03-14

如果我理解正确,你正在寻找这样的东西:

List<Product> results = products.stream()
                        .filter(prod -> predicates.stream()
                                        .anyMatch(pred -> pred.test(prod)))
                        .collect(Collectors.toList());
景正文
2023-03-14

您当前的解决方案将对集合进行多次迭代,但是由于< code>findFirst是一个短路操作符,它将在找到匹配项后立即停止。你是否对它进行了基准测试,以确保它不够好?

另一种方法是使用有状态过滤器(见本文顶部答案):

public static Predicate<Product> matchAndDiscard(final List<Predicate<Product>> predicates) {
  final Set<Predicate<Product>> remaining = new HashSet<>(predicates);
  return product -> {
    final var match = remaining.stream().filter(pred -> pred.test(product)).findFirst();
    match.ifPresent(remaining::remove);
    return match.isPresent();
  };
}

很像@Chaosfire的方法,但包含在过滤器函数中的状态。如果您相信所有谓词都会被至少一个产品匹配,您还可以通过将流限制为谓词的数量来节省一些时间,如下所示:

final var predicates = getPredicates()
final var result = getProducts().stream()
    .filter(matchAndDiscard(predicates))
    .limit(predicates.size())
    .toList()

在您当前的解决方案中,您将“水平”遍历产品:

       --> products
pred1: ffffffffffffft
pred2: fffft
pred3: ffffffffffffffft
pred4: ft
etc.

另一种方法是进行“垂直”遍历:

           products
pred1: | ffffffffffffft
pred2: | fffft
pred3: v ffff fffffffffft
pred4:   ft

因此,一个比另一个快得多并不明显,这取决于特定的配置。

国兴贤
2023-03-14

反过来做怎么样?对产品进行流处理,并对它们应用谓词。

List<Predicate<Product>> predicates = getPredicates();
List<Product> products = getProducts();
List<Product> filtered = products.stream().filter(product -> {
    Iterator<Predicate<Product>> iterator = predicates.iterator();
    while (iterator.hasNext()) {
        Predicate<Product> currentPredicate = iterator.next();
        if (currentPredicate.test(product)) {
             iterator.remove();
             return true;
        }
    }
    return false;
}).collect(Collectors.toList());

缺点是您必须小心将哪个集合用于谓词,并不总是支持Iterator.remove

编辑:看来我读得不够仔细。我认为使用循环时,每种方法各取一个效果最好。

List<Product> products = getProducts();
List<Predicate<Product>> predicates = getPredicates();
List<Product> matchingProducts = new ArrayList<>(predicates.size());
for (Product product : products) {
    if (predicates.isEmpty()) {
        break;
    }
    for (int predicateIndex = 0; predicateIndex < predicates.size(); predicateIndex++) {
        Predicate<Product> predicate = predicates.get(predicateIndex);
        if (predicate.test(product)) {
            matchingProducts.add(product);
            predicates.remove(predicateIndex);
            break;
        }
    }
}

实际上,设法通过一条流来实现它,并花了时间,你是对的,本杰明。

List<Predicate<Product>> predicates = getPredicates();
List<Product> products = getProducts();
List<Product> matches = products.stream()
        .takeWhile(product -> !predicates.isEmpty())
        .filter(product -> {
            Iterator<Predicate<Product>> iterator = predicates.iterator();
            while (iterator.hasNext()) {
                if (iterator.next().test(product)) {
                    iterator.remove();
                    return true;
                }
            }
            return false;
        })
        .collect(Collectors.toList());

只需确保takwhilefilter之前,否则会跳过最后一个匹配元素。

 类似资料:
  • 我刚刚开始玩Java 8 lambda,我正在尝试实现一些我在函数式语言中习惯的东西。 例如,大多数函数语言都有某种对序列进行操作的find函数,或返回第一个元素的列表,该元素的谓词为。我能看到的在《Java八号》中实现这一点的唯一方法是: 然而,这似乎没有效率,因为过滤器将扫描整个列表,至少在我的理解(这可能是错误的)。有更好的办法吗?

  • 我想要找到合适的正则表达式匹配的行数。输入是通过Java Stream插入的日志文件。我想对这个流应用多个过滤器,但每隔一段时间计算一次。

  • 我陷入了java流操作的边缘案例... 我想对以下行为进行编码:“从一个任意的水果篮子中,收集20个最小的,除了最小的梨,因为我们不想那样。” 额外的奖励:来的篮子可能没有任何梨。 null 我不能使用本地布尔值并在筛选第一个pear后将其设置为,因为lambda中的所有局部变量都必须是final的。 最坏的情况是,我可以将篮子一分为二,梨和非梨,对梨进行排序,如果有的话,适当地将它们子列表。这看

  • 问题内容: 我想要一种惯用的方式来找到与谓词匹配的列表中的第一个元素。 当前代码非常丑陋: 我已经考虑过将其更改为: 但是必须有一些更优雅的方法……如果返回一个值而不是没有找到匹配项引发异常,那将是一个很好的选择。 我知道我可以像这样定义一个函数: 但是,如果已经有内置的插件开始用这样的实用函数填充代码,这是很鸡肋的(人们可能不会注意到它们已经在那里,因此随着时间的推移它们会不断重复出现)。 问题

  • 问题内容: 我想做类似的事情: Python的标准库中是否有类似行为? 我知道在这里自己动手很容易,但是我正在寻找一种更标准的方法。 问题答案: 您可以使用filter方法: 或列表理解: 要查找单个元素,可以尝试: 尽管如果没有匹配项将引发异常,因此您可能希望将其包装在try / catch中。方括号()使之成为生成器表达式,而不是列表理解。 就我个人而言,尽管我只是使用常规的过滤器/理解并采用

  • 使用java stream,我有一个ClassA列表,应用于流以进行过滤,所以使用List。stream()。过滤器(谓词)。这样,谓词就需要ClassA的谓词来应用,但我真的想要String的谓词,因为ClassA。Field1是字符串类型。因此,与其让谓词包含f- 缺少的链接是什么,我需要如何转换流才能做到这一点? 对于其他上下文,在过滤之后,我需要从过滤列表中提取第二个字段,因此目标是拥有C